react onChange 输入一个字符触发2次调用?

为什么下面代码输入一个字符后,console 会打印两次,但是把useState({}) 改成 useState(3),state从对象改成原始类型只会打印一次

import React, { useState } from "react";

export default function Child() {
  const [state, setState] = useState({});

  const onChange = () => {
    console.log("handleOnFocus", state);
  };

  return (
    <div>
      <input type="text" onChange={onChange} />
    </div>
  );
}

https://codesandbox.io/p/sandbox/react-input-functions-onfocu...
希望能解释state是对象和原始类型表现不一样的原因.

怀疑是sandbox 的bug,就中间console 打印2次。有时改了代码热更新后打印1次,刷新又打印2次。
image.png

阅读 1.8k
avatarAI BotBETA

回答

在React中,当useState的初始状态是一个对象(如{})时,并且该对象在组件的渲染之间保持不变(即引用没有改变),但对象内部的属性可能由于其他状态更新或副作用(side-effects)而发生变化,这可能导致一种看似onChange被调用了多次的错觉,实际上这是由于React的严格模式(Strict Mode)导致的。

React的严格模式会故意对类组件和函数组件进行两次挂载和更新过程的调用,以帮助开发者识别不安全的生命周期或副作用(side-effects)的使用。但是,这种双重调用不会影响到组件的状态(state)或属性(props)的更新逻辑,它仅仅是在开发环境中模拟了两次渲染过程。

然而,当组件的useState使用了一个对象作为初始状态时,由于对象的引用在组件的整个生命周期中保持不变(即使对象内部属性改变),如果你在onChange或其他副作用中打印了state,并且这些打印操作依赖于React的渲染过程(如严格模式下的双重渲染),你可能会看到两次相同的输出,但这并不意味着onChange事件处理器被调用了两次。

useState的初始状态改为原始类型(如3)时,由于原始类型的值是不可变的,且每次状态更新都会创建一个新的值,因此不会受到React严格模式双重渲染的影响,看起来就像是onChange只被调用了一次。

结论

实际上,onChange事件处理器在两种情况下都只被调用了一次。但在使用对象作为状态时,由于React的严格模式和对象的引用不变性,可能会导致开发者在控制台看到两次相同的输出。要解决这个问题,可以确保不要在副作用(如事件处理器)中打印或依赖于可能在React渲染过程中变化的状态值,除非你有明确的理由这样做。

解决方案

如果你需要在事件处理器中看到最新的状态值,确保你的状态更新逻辑是正确的,并且理解React的严格模式如何影响你的开发环境。如果确实需要调试或跟踪状态变化,考虑使用React DevTools或在useEffect中打印状态变化,这样可以更准确地反映状态的实际变化过程。

1 个回答

打印多次是React有意为之滴,看文档 https://legacy.reactjs.org/docs/strict-mode.html#detecting-un...

StrictMode模式, React会进行两次渲染来帮助开发者发现潜在的问题,只会在开发环境存在。

第一次渲染用于检测副作用,第二次渲染用于实际的DOM更新,原始类型数据属于非引用类型没有副作用,所以只会渲染一次

文档部分内容:
React 假设您编写的每个组件都是纯函数。这意味着您编写的 React 组件在给定相同输入(props、state 和 context)的情况下必须始终返回相同的 JSX。

违反此规则的组件会行为不可预测并导致错误。为了帮助您找到意外不纯的代码,严格模式会在开发过程中两次调用某些函数(仅调用应为纯的函数)

推荐问题
宣传栏