Я налаштував би це так, щоб ви покладалися на змінну глобального стану, щоб сказати своїм компонентам, коли потрібно відображати. Redux краще для цього сценарію, коли багато компонентів розмовляють один з одним, і ви згадали в коментарі, що ви інколи використовуєте його. Тож я намалюю відповідь за допомогою Redux.
Ви повинні були б перемістити API викликів в батьківський контейнер, Component A
. Якщо ви хочете, щоб ваші онуки здійснювали візуалізацію лише після завершення викликів API, ви не можете зберігати ці дзвінки API у власних онуків. Як можна зробити дзвінок API з компонента, який ще не існує?
Після того, як всі виклики API здійснені, ви можете використовувати дії для оновлення змінної глобального стану, що містить купу об'єктів даних. Щоразу, коли дані отримуються (або виявляється помилка), ви можете відправити дію, щоб перевірити, чи повністю заповнений ваш об'єкт даних. Після її повного заповнення ви можете оновити loading
змінну до false
та умовно візуалізувати ваш Grid
компонент.
Так, наприклад:
// 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 })
Ваш редуктор утримує об’єкт глобального стану, який скаже, чи все вже готове до роботи чи ні:
// 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
}
}
}
У ваших діях:
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
}
}
}
Убік
Якщо ви дійсно одружені з ідеєю, що ваші дзвінки API живуть у кожного онука, але вся сітка онуків не надає, поки всі виклики API не будуть завершені, вам доведеться використовувати зовсім інше рішення. У цьому випадку вашим онукам потрібно було б відмовитись з самого початку, щоб здійснити їх дзвінки, але мати клас css display: none
, який змінюється лише після того, як глобальна змінна стан loading
буде позначена як помилкова. Це теж можливо, але начебто, крім точки Реагування.