React:为什么每次重新渲染时调用初始化函数时,从 useState 返回的状态保持不变?

Joj*_*oji 4 javascript reactjs

这是现场演示: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>
    </>
  );
}
Run Code Online (Sandbox Code Playgroud)

每次我单击该按钮时,计数器都会增加并且组件将重新渲染。然后fn()会再次调用,然后会返回一个数组。并在发生变化useEffect时触发nonprimitive。我有两个问题:

  1. 为什么useEffect只触发一次,而不是每次后续调用都触发?nonprimitive是一个数组,它是非原始类型,因此即使其中的字符串项保持不变,它每次也应该不同。React 似乎正在幕后进行一些深入的比较。
  2. 为什么useState每当组件重新渲染时,gets 都会打印两次?当我按下按钮时,即 console.log("useState");内部被调用两次。fn

Ela*_*ger 8

为什么useEffect只触发一次,而不是每次后续调用都触发?nonprimitive是一个数组,它是非原始类型,因此即使其中的字符串项保持不变,它每次也应该不同。React 似乎正在幕后进行一些深入的比较。

重点useState是在组件的渲染中保留值。组件第一次渲染时useState(fn())被调用并nonprimitive获取 value ["string"]。在后续渲染中,useState(fn())再次调用(因为毕竟它仍然是标准函数调用,因此日志记录再次发生),但 React 知道这不是您的第一次渲染,因此它会丢弃参数(fn) 并返回先前存储的值,该值在引用上是相同的。

如果你不希望 React 在每次渲染时调用你的初始状态创建(例如,如果你的初始状态非常复杂或计算起来非常耗时),你可以向它传递一个函数,它只会在初始渲染时调用。

const [nonprimitive, setNonprimitive] = useState(() => fn());
Run Code Online (Sandbox Code Playgroud)

在您的情况下,由于初始状态已经包装在函数中,因此您可以等效地编写

const [nonprimitive, setNonprimitive] = useState(fn);
Run Code Online (Sandbox Code Playgroud)

为什么useState每当组件重新渲染时,gets 都会打印两次?当我按下按钮时,即console.log("useState");内部被调用两次。fn

如果您查看 index.js,您会发现您的App组件被包装在<React.StrictMode>. React 严格模式将有意多次运行某些渲染,以帮助您捕获代码中的副作用和钩子违规。