反应上下文:TypeError:无法读取未定义的属性“ areResultsVisible”

时间:2019-11-02 12:12:33

标签: reactjs react-context

有人可以帮我吗?我正在尝试通过上下文将数据从一个组件传递到另一个组件(从Search.js到Container.js)。但是,我收到类型错误。我在Web上搜索了许多问题,但没有找到答案。抱歉,如果这是孩子气的问题,我是新手。

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 在Header.js附近的 App.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>

Error screenshot My folder tree

3 个答案:

答案 0 :(得分:0)

您首先需要用上下文提供程序(Search)包装顶级组件,因为上下文api的重点是自上而下传递数据,而不必显式传递每个级别的数据,否则上下文使用者首先将没有任何上下文可以使用:

<Search>
  <App />
</Search>

此外,您需要使用prop value来传递提供者的上下文,而不要使用任何prop(例如代码中的searchValue):

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

答案 1 :(得分:0)

问题是您的Container组件不在SearchContext.Provider中。为了确保您的代码正确运行,您必须在应用程序组件内部直接包含SearchContext Provider,以便它既是Search组件又是Container组件的共同父代。

首先,在另一个文件中创建一个上下文

// 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>
    )
  }
}

也不,您可以像这样在App.js中使用SearchProvider

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>
    );
}

答案 2 :(得分:0)

导入 createContext 之类的

import { React, createContext } from 'react';
export const MyContext = createContext();

这对我有帮助。