React上下文API似乎会重新渲染每个组件

5
我正在尝试在我的应用程序中使用新的Context API,但每次更新上下文时,与之连接的任何组件都会重新渲染。我已经设置了沙箱演示以查看代码和工作问题。当您在输入框中键入时,按钮上下文被呈现,反之亦然。我最初的想法是,如果您在输入框中键入,只会打印出输入上下文。

演示

这是它的工作方式还是我错过了什么? 谢谢, Spencer


我不确定我是否正确理解了问题,但是所有依赖于此上下文的组件都会重新渲染。它被称为ThemeContext。如果您更新主题,您希望所有具有主题的组件都会更新。 - Estus Flask
啊 - 好的。所以如果我不想在文本更改时重新渲染按钮,它们是否需要拥有自己的“CONTEXT API”?我来自redux,那里的所有内容都在存储中,当一个项目更改时,整个存储不会在每个连接的组件中传播重新渲染。 - Spencer Bigum
1
是的,一个不同的情境。如果你需要更多地控制这样的“存储”,Redux 可能是一个更好的选择。 - Estus Flask
2个回答

11

我避免使用React Context API重新渲染的方法:

首先,我将我的组件编写为纯函数组件:

const MyComponent = React.memo(({
    somePropFromContext,
    otherPropFromContext, 
    someRegularPropNotFromContext  
}) => {
    ... // regular component logic
    return(
        ... // regular component return
    )
});

然后我编写了一个函数从上下文中选择属性:

function select(){
  const { someSelector, otherSelector } = useContext(MyContext);
  return {
    somePropFromContext: someSelector,
    otherPropFromContext: otherSelector,
  }
}

我已经编写了我的连接HOC:

function connect(WrappedComponent, select){
  return function(props){
    const selectors = select();
    return <WrappedComponent {...selectors} {...props}/>
  }
}

齐心协力

import connect from 'path/to/connect'

const MyComponent ... //previous code

function select() ... //previous code

export default connect(MyComponent, select)

使用方法

<MyComponent someRegularPropNotFromContext={something} />

演示

在 codesandbox 上的演示

结论

MyComponent 只有当特定上下文中的属性更新为新值时才会重新渲染,如果值相同,则不会重新渲染。此外,它避免了在 MyComponent 中未使用的上下文中的任何其他值上重新渲染。选择器内的代码每次上下文更新时都会执行,但由于它不执行任何操作,所以没有浪费重新渲染 MyComponent


2
这不是违背 Context API 的初衷吗?还不如使用 Redux,因为你正在创建自己的“connect”函数... 添加连接器会带来两个问题:包装地狱和属性钻取。 - Pedro Marques
18
“Pedros之战” - wle8300
对我来说出现了错误:React Hook“...”在函数“select”中被调用,但该函数既不是React函数组件也不是自定义的React Hook函数。 - Hasan Sefa Ozalp
@HasanSefaOzalp,你可能做错了什么,也许在使用它的地方没有导入选择函数。 - pedrobern
这是一些解决方案,但并未回答问题。答案是无论如何都不会从提供者到消费者拯救任何组件。 - windmaomao
使用redux...immer让它比context更好。 - Jeremy

7
这是预期的行为。作为消费者的组件在其提供程序数据更新时重新呈现。此外,shouldComponentUpdate 钩子不适用于 Consumers。
引用 React 的内容 API:
所有作为 Provider 后代的 Consumers 将在 Provider 的 value prop 更改时重新呈现。从 Provider 到其后代 Consumers 的传播不受 shouldComponentUpdate 方法的影响,因此即使祖先组件退出更新,Consumer 也会被更新。
要获取更多信息,请查看 这里

谢谢 - 所以根据这个,我必须需要第二个上下文API,这样当文本更改时按钮不会重新渲染... - Spencer Bigum
1
或者你可以使用 shouldComponentUpdate() 钩子来确定是否需要重新渲染。这应该可以解决你的问题,对吧? - Matthew Barbara
好的,这确实有所帮助,但并没有完全实现我的期望,不过它帮助我澄清了很多事情。我只是出于某种原因期望它能更像Redux那样工作。谢谢! - Spencer Bigum
我刚刚更新了我的答案。如果您认为我的答案是正确的,请随意接受它。 - Matthew Barbara
非常感谢。最终我只能使用Redux来达到同样的效果,哈哈。但是在深入了解Context API的细节过程中,我学到了很多东西。ShouldComponentUpdate对于让行为渲染类似于Redux非常有帮助,尽管Redux是免费的,但这也是一个好的知识点。干杯! - Spencer Bigum

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接