【发布时间】:2020-05-06 18:05:04
【问题描述】:
根据docs:
当组件上方最近的
<MyContext.Provider>更新时,此 Hook 将使用传递给该 MyContext 提供程序的最新上下文值触发重新渲染。即使祖先使用React.memo或shouldComponentUpdate,仍然会使用useContext 从组件本身开始重新渲染。 ... 当上下文值发生变化时,调用 useContext 的组件总是会重新渲染。
在我的 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 可以正常工作,因为它成功更新并反映了对上下文的更改。但是,如代码中所示,我希望能够访问组件其他部分中更新的上下文值,即在 return 函数之外,其中 Context.Consumer 专门使用。我假设使用 useContext 钩子会对此有所帮助,因为每次单击 div 时,我的组件都会使用上下文中的新值重新渲染 - 但事实并非如此。任何帮助弄清楚为什么会这样。
TL;DR:<Context.Consumer> 更新并反映子组件对上下文的更改,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
}
}
【问题讨论】:
-
请记住,只有在消费者组件祖先中没有上下文提供者时才会使用默认上下文值。
-
@nicooga 我认为在这种情况下默认上下文值作为值传递给上下文提供程序,因此无论如何都使用
-
是的,你是对的,但它是多余的。如果您使用
useContext,您也不需要将元素包装在Context.Consumer中。检查这个例子:codesandbox.io/s/react-context-usage-example-04vkj. -
“我现在的挑战是想办法在默认上下文值中包含一个可以修改该值的其他属性的函数 -通常的模式是使用
useReducer()ref 更新上下文,尽管您的设置函数方法可能有效。
标签: javascript reactjs react-hooks gatsby react-context