javascript - 为什么 `useContext` 不重新渲染我的组件?

标签 javascript reactjs react-hooks gatsby react-context

根据 docs :

When the nearest <MyContext.Provider> above the component updates, this Hook will trigger a rerender with the latest context value passed to that MyContext provider. Even if an ancestor uses React.memo or shouldComponentUpdate, a rerender will still happen starting at the component itself using useContext. ... A component calling useContext will always re-render when the context value changes.

在我的 Gatsby JS 项目中,我这样定义我的上下文:

Context.js

import React from "react"

const defaultContextValue = {
  data: {
    filterBy: 'year',
    isOptionClicked: false,
    filterValue: ''
  },
  set: () => {},
}

const Context = React.createContext(defaultContextValue)

class ContextProviderComponent extends React.Component {
  constructor() {
    super()

    this.setData = this.setData.bind(this)
    this.state = {
      ...defaultContextValue,
      set: this.setData,
    }
  }

  setData(newData) {
    this.setState(state => ({
      data: {
        ...state.data,
        ...newData,
      },
    }))
  }

  render() {
    return <Context.Provider value={this.state}>{this.props.children}</Context.Provider>
  }
}

export { Context as default, ContextProviderComponent }

在包含多个组件的layout.js 文件中,我放置了上下文提供程序:

Layout.js:

import React from 'react'
import { ContextProviderComponent } from '../../context'

const Layout = ({children}) => {

    return(
        <React.Fragment>
            <ContextProviderComponent>
                {children}
            </ContextProviderComponent>
        </React.Fragment>
    )
}

在我希望使用上下文的组件中:

import React, { useContext } from 'react'
import Context from '../../../context'

const Visuals = () => {

    const filterByYear = 'year'
    const filterByTheme = 'theme'

    const value = useContext(Context)
    const { filterBy, isOptionClicked, filterValue } = value.data

    const data = <<returns some data from backend>>

    const works = filterBy === filterByYear ? 
        data.nodes.filter(node => node.year === filterValue) 
        : 
        data.nodes.filter(node => node.category === filterValue)

   return (
        <Layout noFooter="true">
            <Context.Consumer>
                {({ data, set }) => (
                    <div onClick={() => set( { filterBy: 'theme' })}>
                       { data.filterBy === filterByYear ? <h1>Year</h1> : <h1>Theme</h1> }
                    </div>
                )
            </Context.Consumer>
        </Layout>
    )

Context.Consumer工作正常,因为它成功更新并反射(reflect)了上下文的更改。然而,如代码中所示,我希望能够访问组件其他部分中更新的上下文值,即 return 之外的值。函数其中 Context.Consumer被专门使用。我假设使用 useContext hook 会对此有所帮助,因为每次单击 div 时,我的组件都会使用上下文中的新值重新渲染 - 但事实并非如此。任何帮助弄清楚为什么会出现这种情况的帮助将不胜感激。

TL;博士:<Context.Consumer>更新并反射(reflect)子组件 useContext 对上下文的更改尽管组件需要它,但不会。

更新: 我现在发现useContext将从传递给 createContext 的默认上下文值中读取并且基本上将独立于 Context.Provider 运行。这就是这里发生的事情,Context.Provider包括修改状态的方法,而默认上下文值则不会。我现在的挑战是找出一种方法,将一个函数包含在默认上下文值中,该函数可以修改该值的其他属性。就目前情况而言:

const defaultContextValue = {
  data: {
    filterBy: 'year',
    isOptionClicked: false,
    filterValue: ''
  },
  set: () => {}
}

set是一个空函数,定义在ContextProviderComponent中(往上看)。我如何(如果可能)直接在上下文值中定义它,以便:

const defaultContextValue = {
  data: {
    filterBy: 'year',
    isOptionClicked: false,
    filterValue: ''
  },
  test: 'hi',
  set: (newData) => {
     //directly modify defaultContextValue.data with newData
  }
}

最佳答案

您无需同时使用 <Context.Consumer>useContext钩。

通过使用useContext钩子(Hook)你可以访问存储在 Context 中的值。

关于您的具体示例,在 Visuals 组件中使用上下文的更好方法如下:

import React, { useContext } from "react";
import Context from "./context";

const Visuals = () => {
  const filterByYear = "year";
  const filterByTheme = "theme";

  const { data, set } = useContext(Context);
  const { filterBy, isOptionClicked, filterValue } = data;

  const works =
    filterBy === filterByYear
      ? "filter nodes by year"
      : "filter nodes by theme";

  return (
    <div noFooter="true">
      <div>
        {data.filterBy === filterByYear ? <h1>Year</h1> : <h1>Theme</h1>}
        the value for the 'works' variable is: {works}
        <button onClick={() => set({ filterBy: "theme" })}>
          Filter by theme
        </button>
        <button onClick={() => set({ filterBy: "year" })}>
          Filter by year
        </button>
      </div>
    </div>
  );
};

export default Visuals;

此外,您似乎没有使用 works组件中的变量可能是您未获得所需结果的另一个原因。

您可以查看使用上述 useContext 实现的工作示例这与您在 sandbox 中的示例有些相似

希望这有帮助。

关于javascript - 为什么 `useContext` 不重新渲染我的组件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59828215/

相关文章:

javascript - 为什么 Backbone.js 路由器不能在 iOS 中工作?

javascript - 使用 React 管理 LTE 仪表板

reactjs - 使用 next.js 时 typescript 上的错误数据类型

javascript - 如何将 Azure B2C 与 React 集成

javascript - 将实时更新传递给 Stripe Checkout 金额

javascript - 创建组件时如何以及在何处将 JSON 数据作为 prop 传递

javascript - 其他 amp-accordion 内的 amp-accordion 不工作

reactjs - TypeScript - React 上下文不适用于新的 PropTypes 包

javascript - 如何在 Reactjs 中将 JavaScript 与 $ inside render() 一起使用

reactjs - useEffect props 回调函数导致无限循环