Consuming from React Context returns Cannot read property 'map' of undefined

Viewed 931

I am trying to pass data between two components in React using Context. The data that I want to pass is an array searchResults. I have setup the context and everything, but when I try to use the array in my second component I get Cannot read property 'map' of undefined, how can I consume the array in my second component that I have sent via the context?

Here is my first component:

import React, { Component } from "react";
import axios from "axios";
export const SearchContext = React.createContext();

class Search extends Component {
    state = {
        searchResults: [],
        isSearched: false
    }


    getSearchQuery = (event) => {
        const queryString = document.querySelector(
            ".search-input"
        ).value;

        if (event.keyCode === 13) {
            axios.post("http://localhost:3001/search", {
                queryString: queryString,


            }).then(response => {

                this.setState({ ...this.state, searchResults: response.data });

            });
            this.setState({ ...this.state, isSearched: true });
            window.location.href = '/blog/searchResults'
        }
    };

    render() {
        console.log(this.state.searchResults)
        return (
            <SearchContext.Provider value={this.state.searchResults}>
                <input
                    type="text"
                    className="search-input"
                    onKeyDown={(e) => this.getSearchQuery(e)}
                />
            </SearchContext.Provider>

        );
    }
}

export default Search;

and my second component that should use the array:

import React, { Component } from 'react';
import Footer from '../Footer/Footer.jsx';
import CustomHeader from '../CustomHeader/CustomHeader.jsx';
import { SearchContext } from '../../components/Search/Search.jsx';
let title = 'Blog'

class SearchResultsPage extends Component {
    render() {
        return (
            <div>
                <CustomHeader
                    title={title}
                />

               <SearchContext.Consumer>
                {(value) => value.map(result => (
                    <div key={result._id}>
                        <div>
                        </div>
                        <article>
                            {value.postContent}
                        </article>
                    </div>
                  )
                )}
            </SearchContext.Consumer>
                <Footer />
            </div>
        )
    }
};
2 Answers

I think need return

<SearchContext.Consumer>
                    {value.map(result => {
                    return (    <div key={result._id}>
                            <div>
                            </div>
                            <article>
                                {value.postContent}
                            </article>
                        </div>

                    )}
                    )}
                </SearchContext.Consumer>

Context consumer takes a function as a child to which it passes the context value. You would write it like below

<SearchContext.Consumer>
                {(value) => value.map(result => (
                    <div key={result._id}>
                        <div>
                        </div>
                        <article>
                            {value.postContent}
                        </article>
                    </div>
                  )
                )}
            </SearchContext.Consumer>

Update: Based on your edit

Your SearchResultsPage isn't rendered as a child of Search component and for a context consumer to properly work it needs a context provider in the hierarchy.

Please render SearchResults page like

<Search>
    <SearchResultsPage />
</Search>

and also in Search component you will have to consume and render the children

render() {
    console.log(this.state.searchResults)
    return (
        <SearchContext.Provider value={this.state.searchResults}>
            <input
                type="text"
                className="search-input"
                onKeyDown={(e) => this.getSearchQuery(e)}
            />
            {this.props.children}
        </SearchContext.Provider>

    );
}
Related