Eu o configuraria para que você confie em uma variável de estado global para informar seus componentes quando renderizar. O Redux é melhor para esse cenário em que muitos componentes estão conversando entre si e você mencionou em um comentário que o usa algumas vezes. Então, esboçarei uma resposta usando o Redux.
Você precisaria mover suas chamadas de API para o contêiner pai Component A. Se você deseja que seus netos sejam renderizados somente após a conclusão das chamadas da API, não será possível mantê-las nos próprios netos. Como uma chamada de API pode ser feita a partir de um componente que ainda não existe?
Depois que todas as chamadas de API são feitas, você pode usar ações para atualizar uma variável de estado global que contém vários objetos de dados. Sempre que os dados são recebidos (ou um erro é detectado), você pode despachar uma ação para verificar se o seu objeto de dados está totalmente preenchido. Uma vez preenchido completamente, você pode atualizar uma loadingvariável falsee renderizar condicionalmente seu Gridcomponente.
Então, por exemplo:
// 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 })
Seu redutor conterá o objeto de estado global, que dirá se tudo já está pronto para ser executado:
// 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
}
}
}
Em suas ações:
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, de lado
Se você é realmente casado com a ideia de que suas chamadas à API residem em cada neto, mas que toda a grade de netos não é processada até que todas as chamadas sejam concluídas, você precisará usar uma solução completamente diferente. Nesse caso, seus netos precisariam ser renderizados desde o início para fazer suas chamadas, mas terão uma classe css com display: none, que somente será alterada após a variável de estado global loadingser marcada como falsa. Isso também é possível, mas além do ponto de React.