如何让React组件等待数据获取完成后再渲染子组件?
Hey there! Let's break down your problem and fix that annoying cannot read property "includes" of undefined error.
First, the root cause of this error is that when your Dashboard component renders right after submitting the form, either:
- Your Redux store's
cardsstate is undefined (not an empty array) while thefetchCardsrequest is still pending, or - One of the cards in your
cardsarray has an undefinedtitle(likely the newly submitted one, if your POST API doesn't return the title properly).
Here's how to fix this step by step:
1. Make sure cards is always an array in Redux
The first thing to check is your cards reducer's initial state. If it's set to undefined instead of an empty array, your component will try to call .filter() on undefined before fetchCards completes. Update your reducer to start with an empty array:
// Your cards reducer const initialState = { cards: [], // Critical: start with empty array, not undefined loading: false, // Optional but useful for loading states error: null }; export default function cardsReducer(state = initialState, action) { switch(action.type) { case FETCH_CARDS_START: return {...state, loading: true}; case FETCH_CARDS_SUCCESS: return {...state, loading: false, cards: action.payload}; case SUBMIT_CARD_SUCCESS: // Add the new card safely to the array return {...state, cards: [...state.cards, action.payload]}; // Handle other actions like errors here default: return state; } }
This ensures this.props.cards is always an array (even empty) when your component renders, so .filter() won't throw an error.
2. Add null checks in your filter logic
Even if cards is an array, if a card has no title (like the newly submitted one if your API returns incomplete data), calling .includes() on undefined will throw the error you're seeing. Update your Dashboard's render method to add safety checks:
render() { const filterValue = this.state.filterText.trim().toLowerCase(); const cardList = this.props.cards.filter(card => { // Skip cards that don't exist or have no title if (!card || !card.title) return false; // Make filtering case-insensitive by lowercasing the card title too return card.title.toLowerCase().includes(filterValue); }); return ( <div> <CardList cards={cardList} handleChange={this.handleChange} /> </div> ); }
I also added lowercase conversion for the card title to make your filter case-insensitive (a nice UX touch!).
3. Optional: Add a loading state (for better UX)
To make it clearer to users that data is loading, you can add a loading indicator. First, update your fetchCards action to dispatch loading states:
export const fetchCards = () => async dispatch => { dispatch({ type: FETCH_CARDS_START }); try { const res = await axios.get('/api/cards'); dispatch({ type: FETCH_CARDS_SUCCESS, payload: res.data }); } catch(err) { dispatch({ type: FETCH_CARDS_ERROR, error: err }); } };
Then update your Dashboard's render method to show a loading message while data is being fetched:
render() { const { cards, loading } = this.props; const filterValue = this.state.filterText.trim().toLowerCase(); if (loading) { return <div className="text-center">Loading cards...</div>; } const cardList = cards.filter(card => { if (!card || !card.title) return false; return card.title.toLowerCase().includes(filterValue); }); return ( <div> <CardList cards={cardList} handleChange={this.handleChange} /> </div> ); }
4. Double-check your POST API response
Finally, make sure your /api/cards POST endpoint returns the full card object including the title field. When you submit the form, you dispatch SUBMIT_CARD_SUCCESS with res.data—if that data doesn't include title, the new card in your Redux store will have an undefined title until you refresh (and fetchCards pulls the complete data from the GET endpoint).
These changes should eliminate the error and make your Dashboard render smoothly right after form submission, no manual refresh needed!
内容的提问来源于stack exchange,提问作者Jameson

