React 中 onChange 事件觸發多次:深入探討
在 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 ( <div> <input type="text" onChange={onChange} /> </div> ); }
問題分析
此問題的根源在于 React 的 Strict Mode(嚴格模式)。在開發環境中,Strict Mode 會執行兩次渲染,以幫助開發者發現潛在問題,例如不必要的副作用。
當狀態為對象類型時,setState 更新的是對象的引用,而非值本身。Strict Mode 的雙重渲染會導致 onChange 事件被調用兩次,每次都更新相同的對象引用。 而原始類型狀態(如字符串、數字)直接更新值,因此不會出現這個問題。
根本原因
- 對象類型狀態的引用更新: 使用對象作為狀態時,setState 會創建一個新的對象,但 onChange 函數內的 console.log 仍然打印的是舊狀態,因為 React 的異步更新機制。第二次渲染時,狀態才更新為新值。
- Strict Mode 的雙重渲染: 開發環境下的 Strict Mode 觸發了雙重渲染,加劇了這個問題。
解決方案
避免使用對象類型狀態,或者優化 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 ( <div> <input type="text" value={inputValue} onChange={onChange} /> </div> ); }
方法二:使用函數式更新
使用函數式更新 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 ( <div> <input type="text" onChange={onChange} /> </div> ); }
通過以上方法,可以有效解決 React 中 onChange 事件觸發多次的問題。 記住,生產環境中 Strict Mode 會被禁用,因此這個問題通常只在開發環境中出現。
? 版權聲明
文章版權歸作者所有,未經允許請勿轉載。
THE END