我试图理解这篇(主要是非常有用的(文章,它描述了如何使用 react 的上下文 API 来管理应用程序级状态。对于简单应用程序(在本例中为基本计数器应用程序(,它使用以下解决方案:
const CountContext = React.createContext()
function CountProvider(props) {
const [count, setCount] = React.useState(0)
const value = React.useMemo(() => [count, setCount], [count])
return <CountContext.Provider value={value} {...props} />
}
提供上下文,然后是可以在组件树下方某处的组件中使用的以下钩子:
function useCount() {
const context = React.useContext(CountContext)
if (!context) {
throw new Error(`useCount must be used within a CountProvider`)
}
return context
}
我的问题:
我正在努力理解为什么这里需要useMemo
钩子。这里不涉及特别繁重的计算,所以我不确定我们为什么要记住这些值。如果上下文提供程序如下所示,这是否也同样有效:
function CountProvider(props) {
const [count, setCount] = React.useState(0)
return <CountContext.Provider value={value} {...props} />
}
我觉得我可能错过了什么!!
有一个非常简单的理论来解释为什么你应该使用useMemo
来记住传递给Context Provider
的值。当您将值传递给Context Provider
时,可以作为对象或数组,如下所示:
return <CountContext.Provider value={{state, setCount}} {...props} />
或
return <CountContext.Provider value={[state, setCount]} {...props} />
本质上发生的情况是,每次CountProvider
组件重新呈现对对象或数组的新引用时,都会作为值传递给CountContext.Provider
,因此即使实际值可能没有更改,Context 使用者也会重新呈现,因为该值的引用检查失败。
现在,您可能需要也可能不需要useMemo
,具体取决于ContextProvider
中的逻辑。例如,在您的情况下,CountContext
只是使用一种状态,即 count 并将其传递给子元素,如果CountContext
是顶级元素之一,除了计数更改之外不会重新呈现,那么在这种情况下,无论您是否使用useMemo
都没有区别,因为来自useMemo
的返回值的引用也会在计数更改时更新
但是,如果您有某些父项要CountProvider
这可能会导致CountProvider
重新渲染,那么记住上下文值useMemo
会派上用场,以避免重新渲染所有上下文使用者
调用 setCount 将始终重新渲染组件
我相信调用 setCount 将始终重新渲染,即使将相同的值传递给函数。
但是这条线
const value = React.useMemo(() => [count, setCount], [count])
将停止调用 setCount,除非计数有不同的值。从而减少重新渲染并提高性能。
您可以通过在里面放一个日志并查看组件在有和没有useMemo的情况下如何呈现来测试这个理论。
React.useMemo
从来都不是必需的,因为它是一种性能优化。
如果您不使用 useMemo,它会触发不必要的重新渲染的想法是错误的。React 仅在组件树上的某个位置的状态更改后重新渲染组件。
以下是不会导致不必要的重新渲染的证据: https://codesandbox.io/s/stupefied-franklin-5jf5ke?file=/src/App.tsx
唯一的例外是当组件包装在React.memo()
中时。在这种情况下,对道具或传递给 Context.Provider 的值useMemo
可以防止不必要的重新渲染。
将上下文的值包装在useMemo中是一种不好的做法吗?不,我认为不是,特别是如果您在其他钩子的依赖项数组中使用该上下文,或者将其提供给记忆组件React.memo()
。