当前位置: 首页 > 知识库问答 >
问题:

反应:为什么每次重新呈现时调用初始化器函数时,useState返回的状态保持不变?

相诚
2023-03-14

以下是现场演示:https://codesandbox.io/s/strange-bash-qj7ld?file=/src/App.js

所以我有一个这样的组件

const fn = () => {
  console.log("useState");
  return ["string"];
};

export default function App() {
  const [counter, setCounter] = useState(0);

  const [nonprimitive, setNonprimitive] = useState(fn());

  useEffect(() => {
    console.log("useEffect");
  }, [nonprimitive]);

  return (
    <>
      <button onClick={() => setCounter(counter + 1)}>Counter {counter}</button>
      <div>{nonprimitive[0]}</div>
    </>
  );
}

每次单击按钮时,计数器都会增加,组件将重新渲染。然后将再次调用fn(),然后它将返回一个数组。当非原语发生变化时,会触发useffect。我有两个问题:

  1. 为什么只触发一次,而不是每次后续调用?非原语是一个数组,它是一个非原语类型,所以它应该每次都不同,即使它里面的字符串项保持不变。React似乎在幕后做了一些深入的比较。
  2. 为什么每当组件重新呈现时,useState会打印两次?即console.log(useState);当我按下按钮时,fn内的会被调用两次。

共有2个答案

傅正豪
2023-03-14

双重渲染是React的结果。严格模式

严格模式不能自动为您检测副作用,但它可以通过使副作用更具确定性来帮助您发现副作用。这是通过故意双重调用以下函数来实现的:

  • 类组件构造函数、呈现和shouldComponentUpdate方法

如果您删除,请做出反应。StrictMode从索引文件中,您将停止看到双重渲染。

至于useffect,您是正确的。有一种深刻的对比正在发生。React能够推断数组没有更改,除非数组的元素已更改。还要记住,提供给useState的任何内容都只在第一次渲染时设置,因此在调用setter之前,数组实际上不会更改,即使多次调用fn

响应严格模式文档:https://reactjs.org/docs/strict-mode.html

周翰池
2023-03-14

为什么只触发一次,而不是每次后续调用?非原语是一个数组,它是一个非原语类型,所以它应该每次都不同,即使它里面的字符串项保持不变。反应似乎在幕后做一些深刻的比较。

useState的全部要点是在组件的呈现中持久化值。第一次组件呈现时,useState(fn())被调用,non基元获取值["string"]。在随后的呈现中,useState(fn())再次被调用(因为毕竟它仍然是一个标准函数调用,所以日志记录再次发生),但是React知道它不是你的第一次呈现,所以它丢弃了参数(返回值为fn),并返回先前存储的值,该值在引用上相同。

如果不希望在每次渲染时调用初始状态创建(例如,如果初始状态非常复杂或计算耗时),可以向其传递一个函数,该函数将仅在初始渲染时调用。

const [nonprimitive, setNonprimitive] = useState(() => fn());

在你的例子中,由于初始状态已经包装在函数中,你可以等价地写

const [nonprimitive, setNonprimitive] = useState(fn);

为什么每当组件重新呈现时,useState会打印两次?即console.log(useState);内的fn在我按下按钮时被调用两次。

如果你查看你的索引。js,您会注意到您的应用程序组件包装在

 类似资料:
  • 问题内容: React有一个叫做useState的钩子,在向功能组件添加状态时使用。 该挂钩API参考状态: useState : 返回一个有状态值,以及一个更新它的函数。 在初始渲染期间,返回的状态()与作为第一个参数()传递的值相同。 该功能用于更新状态。它接受一个新的状态值并排队重新呈现组件。 该阵营文档状态: 作为论点我们要传递什么? 挂钩的唯一参数是初始状态。与类不同,状态不必是对象。如

  • 问题内容: 我这里有一些简单的代码 每次单击按钮,控制台上都会显示2条日志,指示该组件呈现两次。 这是一个codeandbox链接,可以尝试一下。 问题答案: 呈现您的App组件的原因是在其中导致代码在严格模式下运行,并且在严格模式下,控制台显示两次,因为每个功能在开发模式下均运行两次 根据react docs: 严格模式无法自动为您检测副作用,但可以通过使其更具确定性来帮助您发现它们。这是通过有

  • 问题内容: import React, { Component } from ‘react’; 通常我看到的是,如果他使用es6类,人们会在构造函数中执行this.state。如果不是,他可能会使用getinitialstatestate函数放置状态。但是上面的代码(是的,这是一个有效的代码),两者都没有使用。我有2个问题,这里的状态是什么?那是局部变量吗?如果是,为什么没有?prevState来

  • 问题内容: 这是无效的代码: 这是一个愚蠢的示例,但是在静态类构造函数中,我们不能这样做。为什么?是否有充分的理由呢?有人对此有更多了解吗? 因此,我应该这样做的原因是在那里结束构建。 谢谢 问题答案: 我认为原因是初始化程序与字段初始化(以及实例初始化程序的构造函数)一起携带。换句话说,JVM仅识别一个位置来初始化静态字段,因此所有的初始化(无论是否以块为单位)都必须在此完成。 因此,例如,当您

  • 问题内容: 当我运行以下代码时,两个测试用例都变为现实: 预期的行为 test1-成功 test2-失败(按预期该计数将变为3) 实际行为 test1-成功 test2-成功 为什么junit 与每个测试方法都调用。它是junit中的错误或有意提供。 问题答案: 每种测试方法的新实例 对于每种测试方法,将创建Junit的行为的 新实例。 因此,在您的情况下,这两种方法的变量都将具有value ,因

  • 当我在输入元素中输入第一个字符时,我得到“空字符串”。 在我看来,我在输入元素中输入一个字符,触发“onChange”事件,运行函数getTitle,设置“title”变量,该变量连接到useState钩子,然后控制结果。按照这个推理,我希望输入第一个字符。相反,我得到的是“空字符串”。从第二个字符开始,控制台打印字符。 对于“onInput”,函数也会发生同样的情况。 如何解决这个问题,为什么会