Контекст реакции: TypeError: Невозможно прочитать свойство 'areResultsVisible' из неопределенного - PullRequest
0 голосов
/ 02 ноября 2019

Может кто-нибудь, пожалуйста, помогите мне? Я пытаюсь передать данные через контекст из одного компонента в другой (из Search.js в Container.js). Тем не менее, я получаю ошибку типа. Я искал много вопросов в Интернете, но не нашел ответа. Извините, если это детская проблема, я новичок.

Search.js:

import React, { Component } from 'react'
import { StyledFormSearchBar } from '../styles'
import { data } from '../data'

const SearchContext = React.createContext()

export default class Search extends Component {
  constructor() {
    super()
    this.state = {
      value: '',
      results: [],
      areResultsVisible: false
    }

    this.handleSearch = this.handleSearch.bind(this)
  }

  handleSearch(e) {
    this.setState = {
      value: e.target.value,
      results: data.filter(item => {
        return item.title.toLowerCase().includes(e.target.value.toLowerCase())
      }),
      areResultsVisible: true
    }
  }

  render() {
    return (
      <StyledFormSearchBar>
        <input type="search" name="search" className="border border-dark rounded" onSubmit={this.handleSearch}/>
        <button type="submit" value="submit" className="bg-warning border-0 text-danger rounded-right position-relative">
          <i className="fas fa-search"></i>
        </button>

        <SearchContext.Provider value = {{
          ...this.state,
          handleSearch: this.handleSearch
        }}>
          {this.props.children}
        </SearchContext.Provider>
        
      </StyledFormSearchBar>
    )
  }
}

const SearchContextConsumer = SearchContext.Consumer

export { SearchContextConsumer }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Container.js:

import React from 'react'
import { Route, Switch } from "react-router-dom"
import { StyledDivGridContainer } from './styles'
import { SearchContextConsumer } from './header/Search'

import Carousel from './container/Carousel'
import MobilePhonesDiscount from './container/products/carousel/MobilePhonesDiscount'
import LaptopsDiscount from './container/products/carousel/LaptopsDiscount'
import TabletsDiscount from './container/products/carousel/TabletsDiscount'
import Products from './container/Products'
import SearchResults from './container/SearchResults'
import MobilePhones from './container/products/MobilePhones'
import Laptops from './container/products/Laptops'
import Tablets from './container/products/Tablets'
import ProductPage from './container/ProductPage'
import About from './container/About'
import ContactUs from './container/ContactUs'

export default function Container() {
  return (
    <StyledDivGridContainer>
      <div className="no-gutters justify-content-between">
        <SearchContextConsumer>
          {
            value => {
              return (
                !value.areResultsVisible 
                ? <Switch>
                    <Route exact path="/" component={Carousel}/>
                    <Route exact path="/" component={Products}/>
                  </Switch>                
                : <Route exact path="/" component={SearchResults}/>
              )
            }
          }
        </SearchContextConsumer>
        <Route path="/mobile_phones_discount" component={MobilePhonesDiscount}/>
        <Route path="/laptops_discount" component={LaptopsDiscount}/>
        <Route path="/tablets_discount" component={TabletsDiscount}/>
        <Route path="/mobile_phones" component={MobilePhones}/>
        <Route path="/laptops" component={Laptops}/>
        <Route path="/tablets" component={Tablets}/>
        <Route path="/product_page" component={ProductPage}/>
        <Route path="/about" component={About}/>
        <Route path="/contact_us" component={ContactUs}/>
      </div>
    </StyledDivGridContainer>
  )
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Search.js в Header.js :

import React, { Component } from 'react'
import { StyledHeader,
         StyledSpanAccount } from './styles'
import { Link } from "react-router-dom"

import Catalogue from "./header/Catalogue"
import Search from "./header/Search"

export default class Header extends Component {
  render() {
    return (
      <StyledHeader className="d-flex w-100 bg-light shadow justify-content-center">
        <div className="d-flex flex-wrap justify-content-around align-items-center">
          <div className="my-1 mr-3">
            <Link to="/" className="logo">
              <img src={require('../img/logo.webp')} alt="logo" className="img-tumbnail"/>
            </Link>
          </div>
          <Catalogue/>

          <Search/>
          
          <div className="d-flex my-3">
            <a href="#"><i className="fas fa-shopping-cart"></i></a>
            <a href="#"><i className="fas fa-user-alt ml-3"></i></a>
            <a href="#" className="d-flex flex-nowrap">
              <StyledSpanAccount className="ml-2">Log in</StyledSpanAccount>
            </a>
            <a href="#" className="d-flex flex-nowrap">
              <StyledSpanAccount className="ml-2">Sing up</StyledSpanAccount>
            </a>
          </div>
        </div>
      </StyledHeader>
    )
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Container.js в App.js возле Header.js:

import React from 'react';
import { Route } from "react-router-dom";
import { StyledDivWrapper } from './components/styles';

import Header from './components/Header';
import Container from './components/Container';
import Footer from './components/Footer';

export default function App() {
  return (
    <StyledDivWrapper className="d-flex flex-column">
      <Route path="/" component={Header}/>
      <Route path="/" component={Container}/>
      <Route path="/" component={Footer}/>
    </StyledDivWrapper>
  );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Снимок экрана ошибки Дерево моей папки

Ответы [ 2 ]

0 голосов
/ 02 ноября 2019

Проблема в том, что ваш контейнерный компонент не входит в ваш SearchContext.Provider. Чтобы убедиться, что ваш код работает правильно, у вас должен быть поставщик SearchContext непосредственно внутри вашего компонента приложения, чтобы он был общим родителем для компонента поиска и контейнера

Во-первых, создайте контекст в другом файле

// SearchContext.js

export const SearchContext = React.createContext();
const SearchContextConsumer = SearchContext.Consumer

export { SearchContextConsumer }

Во-вторых, создайте компонент, который выступает в качестве поставщика

// SearchProvider.js

class SearchProvider extends React.Component {
   constructor() {
    super()
    this.state = {
      value: '',
      results: [],
      areResultsVisible: false
    }

    this.handleSearch = this.handleSearch.bind(this)
  }

  handleSearch(e) {
    this.setState = {
      value: e.target.value,
      results: data.filter(item => {
        return item.title.toLowerCase().includes(e.target.value.toLowerCase())
      }),
      areResultsVisible: true
    }
  }

  render() {
    return (
       <SearchContext.Provider value = {{
          ...this.state,
          handleSearch: this.handleSearch
        }}>
          {this.props.children}
       </SearchContext.Provider>        
    )
  }
}

Теперь ваш компонент поиска будет просто

// Search.js


export default class Search extends Component {
  render() {
    return (
       <SearchContext.Consumer>
         {({handleSearch}) => (
             <StyledFormSearchBar>
                <input type="search" name="search" className="border border-dark rounded" onSubmit={handleSearch}/>
                <button type="submit" value="submit" className="bg-warning border-0 text-danger rounded-right position-relative">
                  <i className="fas fa-search"></i>
                </button>
              </StyledFormSearchBar>
          )}
        </SearchContext.Consumer>
    )
  }
}

Также вы не можете использовать SearchProvider в приложении. JS как

export default function App() {
    return (
       <SearchProvider>
          <StyledDivWrapper className="d-flex flex-column">
              <Route path="/" component={Header}/>
              <Route path="/" component={Container}/>
              <Route path="/" component={Footer}/>
          </StyledDivWrapper>
       </SearchProvider>
    );
}
0 голосов
/ 02 ноября 2019

Сначала необходимо обернуть ваш компонент верхнего уровня поставщиком контекста (Search), потому что смысл api контекста состоит в том, чтобы передавать данные сверху вниз без явной передачи их через каждый уровень, в противном случае контексту потребителя не будет никакого контекста, чтобы потреблять в первую очередь:

<Search>
  <App />
</Search>

Кроме того, вам нужно использовать пропеллер value для передачи контекста от провайдера, а не проп (например, searchValue)в вашем коде):

<SearchContext.Provider value = {{
    ...this.state,
    handleSearch: this.handleSearch
  }}>
    {this.props.children}
</SearchContext.Provider>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...