为啥反应上下文提供程序组件呈现两次[重复]

Posted

技术标签:

【中文标题】为啥反应上下文提供程序组件呈现两次[重复]【英文标题】:Why react context provider component renders twice [duplicate]为什么反应上下文提供程序组件呈现两次[重复] 【发布时间】:2020-08-15 08:55:59 【问题描述】:

我在玩 React Context API,发现提供程序在每次值更改时呈现两次。

这就是我所做的。

    创建了一个上下文,然后是一个呈现其提供者的组件。 提供程序提供状态值及其设置器。 创建了一个直接渲染其子级的直接子级。 创建了一个消费者,它读取上下文值并添加一个按钮来更改它。 每个组件在呈现时都会执行一个 console.log。 为提供程序添加了一个效果,无需在每次呈现时都记录日志。 在提供程序中添加了一个 ref,该 ref 在每次渲染时递增,并在渲染和效果中记录。

问题?

每次点击按钮更改上下文值时,提供程序渲染两次,但效果只执行一次。

所以 ref 总是递增两次,但效果每次只获取最后一个值(它跳过一个值!)。

此外,在提供程序的第一次渲染时,它会记录两次相同的 ref 值,这对我来说很奇怪。

谁能告诉我为什么会出现这种行为?

代码如下:

提供者:

const MyContext = React.createContext(0);

function Provider( children ) 
  const state = React.useState("Yes");
  const ref = React.useRef(0);
  ref.current += 1;
  console.log("Context provider ", ref.current);
  React.useEffect(() => 
    console.log("effect on provider, ref value = ", ref.current);
  );

  return <MyContext.Provider value=state>children</MyContext.Provider>;

两个孩子

function DirectChild( children ) 
  console.log("provider direct child");
  return children;


function Consumer() 
  console.log("consumer");
  const [state, setState] = React.useContext(MyContext);
  return (
    <div>
      <span>State value: state</span>
      <br />
      <button onClick=() => setState(old => old + "Yes")>Change</button>
    </div>
  );

应用程序

export default function App() 
  console.log("APP");
  return (
    <Provider>
      <DirectChild>
        <Consumer />
      </DirectChild>
    </Provider>
  );

这是codesandbox demo

【问题讨论】:

将状态逻辑移到应用层,现在行为也在App,渲染两次,记录两次,只有一个效果,这里是a demo 我的理解是 refs 用于引用 Dom 对象,而不是 state refs(即useState)。我想你可以用它们来引用任何东西,只是不确定这是他们的意图:"avoid using refs for anything that could be done declaratively". 是的,refs 旨在创建一个可变对象,您可以随意改变它。我找到了这背后的原因,我现在会发布答案;在代码框里是 React.strictMode 并且只影响开发模式 是的,我怀疑是这样的:)我上周左右回答了一个类似的问题...... 【参考方案1】:

找到原因,

没有错误,也没有奇怪的行为,我只是错过了默认情况下在 React.StrictMode 父级中呈现应用程序的代码框。

首先,我将代码移植到本地项目,然后观察到没有问题。

搜索了codesandbox repo问题,发现它与严格模式下的反应行为有关:

预计 setState 更新程序将在开发中以严格模式运行两次。这有助于确保代码不依赖于它们一次运行(如果异步渲染被中止并重新启动,则不会出现这种情况)。如果您的 setState 更新程序是纯函数(它们应该是),那么这不应该影响您的应用程序的逻辑。

Codesandbox issue

React issue

但是,效果只执行一次,错过了我的参考更新。

【讨论】:

如果你想跟踪 ref 值,你可以使用React.useCallback 这是示例` const ref = React.useRef(0);让 setRef = React.useCallback(setValue => ref.current = setValue(ref.current); console.log(ref.current) , []) React.useEffect(() => setRef(old => old + 1) , [状态[0]]);`

以上是关于为啥反应上下文提供程序组件呈现两次[重复]的主要内容,如果未能解决你的问题,请参考以下文章

从树外部以编程方式设置反应上下文提供者状态

反应上下文对象似乎没有再次正确更新[重复]

包含Context.Provider和Context.Consumer的反应上下文测试组件

如何在上下文提供程序中使用 axios get 调用来做出反应

等待反应上下文后组件不重新渲染

在重定向之前需要反应上下文来更新