Lo configuraría para que confíe en una variable de estado global para indicar a sus componentes cuándo renderizar. Redux es mejor para este escenario en el que muchos componentes están hablando entre sí, y usted mencionó en un comentario que a veces lo usa. Entonces esbozaré una respuesta usando Redux.
Habría que mover sus llamadas a la API al contenedor principal, Component A
. Si desea que sus nietos se procesen solo después de que se completen las llamadas API, no puede mantener esas llamadas API en los nietos mismos. ¿Cómo se puede hacer una llamada API desde un componente que aún no existe?
Una vez que se realizan todas las llamadas a la API, puede usar acciones para actualizar una variable de estado global que contiene un montón de objetos de datos. Cada vez que se reciben datos (o se detecta un error), puede enviar una acción para verificar si su objeto de datos está completamente lleno. Una vez que se haya completado por completo, puede actualizar una loading
variable false
y renderizar condicionalmente su Grid
componente.
Así por ejemplo:
// 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 })
Su reductor contendrá el objeto de estado global que dirá si las cosas están listas para funcionar o no:
// 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
}
}
}
En tus acciones:
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
}
}
}
Aparte
Si realmente está casado con la idea de que sus llamadas API viven dentro de cada nieto, pero que la cuadrícula completa de nietos no se procesa hasta que se completen todas las llamadas API, tendría que usar una solución completamente diferente. En este caso, sus nietos tendrían que ser representados desde el principio para hacer sus llamadas, pero tienen una clase css con display: none
, que solo cambia después de que la variable de estado global loading
se marca como falsa. Esto también es factible, pero además del punto de Reaccionar.