在 React 开发中,输入框的 onChange
事件有时会意外触发多次,本文将深入分析此问题,并提供解决方案。
一个简单的 React 组件,使用 useState
hook 管理状态,并在输入框的 onChange
事件中更新状态并打印。然而,输入一个字符,控制台却打印了两次。这种现象在使用对象类型状态时尤其明显,而使用原始类型状态则不会出现。
示例代码片段 (问题代码):
import React, { useState } from "react"; export default function Child() { const [state, setState] = useState({}); const onChange = (event) => { setState({ ...state, value: event.target.value }); console.log("onChange triggered", state); }; return (); }
此问题的根源在于 React 的 Strict Mode(严格模式)。在开发环境中,Strict Mode 会执行两次渲染,以帮助开发者发现潜在问题,例如不必要的副作用。
当状态为对象类型时,setState
更新的是对象的引用,而非值本身。Strict Mode 的双重渲染会导致 onChange
事件被调用两次,每次都更新相同的对象引用。 而原始类型状态(如字符串、数字)直接更新值,因此不会出现这个问题。
setState
会创建一个新的对象,但 onChange
函数内的 console.log
仍然打印的是旧状态,因为 React 的异步更新机制。第二次渲染时,状态才更新为新值。避免使用对象类型状态,或者优化 setState
的调用方式:
方法一:使用原始类型状态
将状态改为原始类型,例如字符串:
import React, { useState } from "react"; export default function Child() { const [inputValue, setInputValue] = useState(""); const onChange = (event) => { setInputValue(event.target.value); console.log("onChange triggered", inputValue); }; return (); }
方法二:使用函数式更新
使用函数式更新 setState
,确保每次更新都基于最新的状态:
import React, { useState } from "react"; export default function Child() { const [state, setState] = useState({}); const onChange = (event) => { setState((prevState) => ({ ...prevState, value: event.target.value })); console.log("onChange triggered", state); }; return (); }
通过以上方法,可以有效解决 React 中 onChange
事件触发多次的问题。 记住,生产环境中 Strict Mode 会被禁用,因此这个问题通常只在开发环境中出现。