React Context传数组报错:Cannot read property 'map' of undefined
Let's break down what's going wrong here and fix it step by step—this is a super common pitfall when using Context alongside routing!
1. The Core Issue: Your Provider Disappears After Navigation
Right now, your SearchContext.Provider is only wrapped around the Search component. When you run window.location.href = '/blog/searchResults', you're navigating away from the page where Search lives. That means the Provider gets unmounted entirely, so your SearchResultsPage's Consumer has no Provider to pull data from—hence value becomes undefined, and calling map on it throws an error.
Fix: Move the SearchContext.Provider to a higher level in your component tree (like your root App component) so it wraps both the Search component and the SearchResultsPage route. This keeps the Context available across navigation.
2. You're Navigating Before the API Call Finishes
Your code triggers the page jump immediately after sending the axios request, before the server response comes back. This not only means your searchResults state never gets updated before the jump, but it might even cancel the API request entirely.
Fix: Move the navigation logic inside the axios .then() callback, so you only jump once you have the data. Also, use React Router's history.push() instead of window.location.href to avoid a full page refresh (which resets your app state).
3. Minor Fixes: Redundant setState & Incorrect Map Reference
- You don't need to spread
this.stateinsetState—React automatically merges state updates for you. - In your Consumer, you tried to access
value.postContentinstead ofresult.postContent(the individual item in the map loop). That would have broken even if the array loaded correctly!
Updated Code Examples
Step 1: Move Provider to Root App Component
// App.js import React, { Component } from "react"; import { BrowserRouter as Router, Route } from 'react-router-dom'; import { SearchContext } from './components/Search/Search'; import Search from './components/Search/Search'; import SearchResultsPage from './components/SearchResultsPage/SearchResultsPage'; class App extends Component { state = { searchResults: [], // Add a method to update search results from child components updateSearchResults: (results) => { this.setState({ searchResults: results }); } }; render() { // Pass both results and update method through Context return ( <SearchContext.Provider value={this.state}> <Router> <Route exact path="/" component={Search} /> <Route path="/blog/searchResults" component={SearchResultsPage} /> </Router> </SearchContext.Provider> ); } } export default App;
Step 2: Update the Search Component
// Search.jsx import React, { Component } from "react"; import axios from "axios"; import { SearchContext } from './Search'; class Search extends Component { // Use contextType to access Context directly (cleaner for class components) static contextType = SearchContext; getSearchQuery = (event) => { const queryString = document.querySelector(".search-input").value; if (event.keyCode === 13) { axios.post("http://localhost:3001/search", { queryString }) .then(response => { // Update global search results via Context this.context.updateSearchResults(response.data); // Navigate only after data is loaded this.props.history.push('/blog/searchResults'); }) .catch(err => console.error("Search failed:", err)); } }; render() { return ( <input type="text" className="search-input" onKeyDown={this.getSearchQuery} /> ); } } export default Search; export const SearchContext = React.createContext();
Step 3: Fix the SearchResultsPage Component
// SearchResultsPage.jsx import React, { Component } from 'react'; import Footer from '../Footer/Footer.jsx'; import CustomHeader from '../CustomHeader/CustomHeader.jsx'; import { SearchContext } from '../../components/Search/Search.jsx'; const title = 'Blog'; class SearchResultsPage extends Component { static contextType = SearchContext; render() { const { searchResults } = this.context; return ( <div> <CustomHeader title={title} /> {/* Guard clause to avoid map on empty/undefined state */} {searchResults.length > 0 ? ( searchResults.map(result => ( <div key={result._id}> <article>{result.postContent}</article> </div> )) ) : ( <p>No search results found.</p> )} <Footer /> </div> ) } }; export default SearchResultsPage;
Key Takeaways
- Always ensure your Context Provider wraps all components that need to consume it, especially across routes.
- Never navigate before async operations (like API calls) complete—wait for the promise to resolve.
- Add guard clauses (like checking
searchResults.length > 0) to handle empty or undefined state gracefully.
内容的提问来源于stack exchange,提问作者Mahma Deva

