根据 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 usesReact.memo
orshouldComponentUpdate
, 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/