如何解决React重渲染时useEffect依赖函数非预期触发问题
React组件重渲染时useEffect异常触发问题解答
核心结论
你的判断完全正确。
原理说明
useEffect的执行遵循固定规则:组件每次重渲染完成后,会对依赖数组内的所有项做浅比较,只要任意一项和上一轮渲染的引用/值不相等,就会执行传入的回调函数;如果不填写依赖数组,回调只会在组件首次挂载时执行1次,后续所有重渲染都不会触发,这就是你移除依赖数组后输入内容变化不打印日志的原因。- 你在组件函数作用域内直接定义的
sum是引用类型的函数值。组件每一次重渲染(比如输入框内容变化触发setInput更新)都会让组件函数从头执行一遍,内部定义的sum函数会被重新创建,分配全新的内存引用地址。此时把sum放入依赖数组,浅比较时会判定依赖发生变化,自然会触发回调执行打印日志。
注:你提供的复现代码中useEffect依赖项写为
[num]属于笔误,以下方案均基于实际将sum作为依赖项的场景给出。
可行解决方案
方案1:直接使用计算值而非函数引用
由于num1、num2是状态值,你不需要额外包裹函数,直接计算和值即可。原始值类型的比较是值比较,只要num1、num2不变化,和值就不会变化,不会触发不必要的effect执行:
import "./styles.css"; import { useEffect, useState } from "react"; export default function App() { const [num1] = useState(4); const [num2] = useState(5); const [input, setInput] = useState(); // 直接计算值,不定义函数 const sum = num1 + num2; useEffect(() => { console.log("sum is", sum); }, [sum]); return ( <div className="App"> <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> <input onChange={(e) => setInput(e.target.value)} /> </div> ); }
方案2:用useCallback缓存函数引用
如果你确实需要保留sum的函数形式,可以用useCallback包裹函数,在依赖项不变时复用同一函数引用,避免重渲染时生成新引用:
import "./styles.css"; import { useCallback, useEffect, useState } from "react"; export default function App() { const [num1] = useState(4); const [num2] = useState(5); const [input, setInput] = useState(); // 缓存sum函数引用,仅当num1/num2变化时才重新生成 const sum = useCallback(() => num1 + num2, [num1, num2]); useEffect(() => { console.log("sum is"); }, [sum]); return ( <div className="App"> <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> <input onChange={(e) => setInput(e.target.value)} /> </div> ); }
方案3:直接将真实变化源作为依赖项
如果effect逻辑只需要在num1或num2变化时触发,完全不需要把sum放入依赖数组,直接将两个状态作为依赖即可:
import "./styles.css"; import { useEffect, useState } from "react"; export default function App() { const [num1] = useState(4); const [num2] = useState(5); const [input, setInput] = useState(); const sum = () => num1 + num2; useEffect(() => { console.log("sum is", sum()); // 直接依赖真实变化的状态 }, [num1, num2]); return ( <div className="App"> <h1>Hello CodeSandbox</h1> <h2>Start editing to see some magic happen!</h2> <input onChange={(e) => setInput(e.target.value)} /> </div> ); }
内容的提问来源于stack exchange,提问作者Lobsang Gyatso
相关产品推荐
相关产品推荐

