Lo configurerei in modo da fare affidamento su una variabile di stato globale per indicare ai componenti quando eseguire il rendering. Redux è meglio per questo scenario in cui molti componenti parlano tra loro e in un commento hai menzionato che lo usi a volte. Quindi disegnerò una risposta usando Redux.
Avresti per spostare le chiamate API al contenitore principale, Component A
. Se desideri che i tuoi nipoti vengano visualizzati solo dopo che le chiamate API sono state completate, non puoi mantenere quelle chiamate API nei nipoti stessi. Come è possibile effettuare una chiamata API da un componente che non esiste ancora?
Dopo aver effettuato tutte le chiamate API, è possibile utilizzare le azioni per aggiornare una variabile di stato globale contenente un gruppo di oggetti dati. Ogni volta che i dati vengono ricevuti (o viene rilevato un errore), è possibile inviare un'azione per verificare se l'oggetto dati è completamente compilato. Una volta compilato completamente, è possibile aggiornare una loading
variabile false
e rendere condizionatamente il Grid
componente.
Quindi per esempio:
// Component A
import { acceptData, catchError } from '../actions'
class ComponentA extends React.Component{
componentDidMount () {
fetch('yoururl.com/data')
.then( response => response.json() )
// send your data to the global state data array
.then( data => this.props.acceptData(data, grandChildNumber) )
.catch( error => this.props.catchError(error, grandChildNumber) )
// make all your fetch calls here
}
// Conditionally render your Loading or Grid based on the global state variable 'loading'
render() {
return (
{ this.props.loading && <Loading /> }
{ !this.props.loading && <Grid /> }
)
}
}
const mapStateToProps = state => ({ loading: state.loading })
const mapDispatchToProps = dispatch => ({
acceptData: data => dispatch( acceptData( data, number ) )
catchError: error=> dispatch( catchError( error, number) )
})
// Grid - not much going on here...
render () {
return (
<div className="Grid">
<GrandChild1 number={1} />
<GrandChild2 number={2} />
<GrandChild3 number={3} />
...
// Or render the granchildren from an array with a .map, or something similar
</div>
)
}
// Grandchild
// Conditionally render either an error or your data, depending on what came back from fetch
render () {
return (
{ !this.props.data[this.props.number].error && <Your Content Here /> }
{ this.props.data[this.props.number].error && <Your Error Here /> }
)
}
const mapStateToProps = state => ({ data: state.data })
Il tuo riduttore tratterrà l'oggetto dello stato globale che dirà se le cose sono ancora pronte per partire:
// reducers.js
const initialState = {
data: [{},{},{},{}...], // 9 empty objects
loading: true
}
const reducers = (state = initialState, action) {
switch(action.type){
case RECIEVE_SOME_DATA:
return {
...state,
data: action.data
}
case RECIEVE_ERROR:
return {
...state,
data: action.data
}
case STOP_LOADING:
return {
...state,
loading: false
}
}
}
Nelle tue azioni:
export const acceptData = (data, number) => {
// First revise your data array to have the new data in the right place
const updatedData = data
updatedData[number] = data
// Now check to see if all your data objects are populated
// and update your loading state:
dispatch( checkAllData() )
return {
type: RECIEVE_SOME_DATA,
data: updatedData,
}
}
// error checking - because you want your stuff to render even if one of your api calls
// catches an error
export const catchError(error, number) {
// First revise your data array to have the error in the right place
const updatedData = data
updatedData[number].error = error
// Now check to see if all your data objects are populated
// and update your loading state:
dispatch( checkAllData() )
return {
type: RECIEVE_ERROR,
data: updatedData,
}
}
export const checkAllData() {
// Check that every data object has something in it
if ( // fancy footwork to check each object in the data array and see if its empty or not
store.getState().data.every( dataSet =>
Object.entries(dataSet).length === 0 && dataSet.constructor === Object ) ) {
return {
type: STOP_LOADING
}
}
}
A parte
Se sei davvero sposato all'idea che le tue chiamate API vivano all'interno di ciascun nipote, ma che l'intera griglia dei nipoti non venga visualizzata fino a quando tutte le chiamate API non sono state completate, dovresti utilizzare una soluzione completamente diversa. In questo caso, i tuoi nipoti dovrebbero essere resi dall'inizio per effettuare le loro chiamate, ma avere una classe CSS con display: none
, che cambia solo dopo che la variabile di stato globale loading
è contrassegnata come falsa. Anche questo è fattibile, ma in qualche modo oltre al punto di React.