Je le configurerais pour que vous vous appuyiez sur une variable d'état globale pour indiquer à vos composants quand effectuer le rendu. Redux est meilleur pour ce scénario où de nombreux composants se parlent et vous avez mentionné dans un commentaire que vous l'utilisez parfois. Je vais donc esquisser une réponse en utilisant Redux.
Il faudrait passer vos appels API au conteneur parent, Component A
. Si vous souhaitez que vos petits-enfants ne s'affichent qu'après la fin des appels d'API, vous ne pouvez pas conserver ces appels d'API dans les petits-enfants eux-mêmes. Comment faire un appel d'API à partir d'un composant qui n'existe pas encore?
Une fois tous les appels d'API effectués, vous pouvez utiliser des actions pour mettre à jour une variable d'état globale contenant un groupe d'objets de données. Chaque fois que des données sont reçues (ou qu'une erreur est détectée), vous pouvez envoyer une action pour vérifier si votre objet de données est entièrement rempli. Une fois complètement remplie, vous pouvez mettre à jour une loading
variable false
et rendre votre Grid
composant sous condition .
Ainsi, par exemple:
// 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 })
Votre réducteur contiendra l'objet d'état global qui dira si tout est prêt ou non:
// 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
}
}
}
Dans vos actions:
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
}
}
}
De côté
Si vous êtes vraiment marié à l'idée que vos appels d'API vivent à l'intérieur de chaque petit-enfant, mais que l'ensemble de la grille des petits-enfants ne s'affiche pas tant que tous les appels d'API ne sont pas terminés, vous devrez utiliser une solution complètement différente. Dans ce cas, vos petits-enfants devraient être restitués dès le début pour effectuer leurs appels, mais avec une classe css avec display: none
, qui ne change qu'après que la variable d'état global loading
est marquée comme fausse. C'est également faisable, mais en plus du point de React.