【问题标题】:React Context: TypeError: Cannot read property 'areResultsVisible' of undefined反应上下文:TypeError:无法读取未定义的属性“areResultsVisible”
【发布时间】:2021-05-24 12:46:50
【问题描述】:

有人可以帮帮我吗?我正在尝试通过上下文将数据从一个组件传递到另一个组件(从 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.jsHeader.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.jsApp.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>

Error screenshot My folder tree

【问题讨论】:

    标签: reactjs react-context


    【解决方案1】:

    问题是您的 Container 组件不在 SearchContext.Provider 中。为了确保您的代码正常工作,您必须将 SearchContext Provider 直接放在您的 App 组件中,以便它是 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>
        );
    }
    

    【讨论】:

    • 非常感谢!我对 context.js 做了类似的事情,但我从来不知道使用 Context API 数据流也应该是自上而下的。
    【解决方案2】:

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

    <Search>
      <App />
    </Search>
    

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

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

    【讨论】:

    • 我在这两个文件中都改了,但错误仍然存​​在。
    • 你在另一个文件中做了什么改变?你能更新你的代码吗?你还确定你用Search包装了你的应用程序?
    • 已更新。我认为 Context 的重点是将数据从任何地方传递到任何地方。 Container.js 未包装,它位于 Search.js 的另一个文件夹中。
    • 上下文的重点是传递数据自上而下,而不必明确地通过每一层,话虽如此,你仍然需要包装你的顶级组件与提供商 (Search)。
    • 我在 context.js 中有另一个 Context API,它运行良好,但它只是位于全局文件夹中,将数据向下传递到文件夹树。我是否也应该将 Search.js 放在全局文件夹中以便使用它?
    【解决方案3】:

    像导入createContext

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

    这对我有帮助。

    【讨论】:

      猜你喜欢
      • 2019-06-12
      • 2022-09-27
      • 2020-09-07
      • 2021-06-22
      • 2021-07-10
      • 2017-01-01
      • 2021-01-16
      • 2021-08-05
      • 1970-01-01
      相关资源
      最近更新 更多