自定义Hook中,为何需将useEffect依赖项中的状态setter函数用括号包裹?
React自定义Hook中useEffect未触发的问题解析
先看你的代码场景:
import { useState, useEffect, useContext } from "react"; import { GlobalContext } from "context/GlobalState"; const useMyCustomHook = () => { const { myGlobalContext } = useContext(GlobalContext); const [myState, setMyState] = useState<MyStateType | null>(null); useEffect(() => { console.log("This code is never triggered."); }, [myGlobalContext, setMyState]); return myState; };
你遇到的情况是:当依赖数组包含setMyState时,useEffect的回调从未执行,但移除它或者把它包裹成[setMyState]后,功能恢复正常。下面是问题的核心原因和正确做法:
核心原因:React对setter函数的稳定性保证
React的useState返回的setter函数(比如这里的setMyState)是全局稳定的——组件每次重新渲染时,同一个useState调用返回的setter引用完全一致,不会发生变化。
原代码的问题本质是对依赖数组的误解:
- 原依赖数组
[myGlobalContext, setMyState]中,setMyState永远不会变化,所以只有当myGlobalContext变化时,useEffect才会触发。如果原场景中myGlobalContext从未发生过变化,回调自然不会执行; - 把
setMyState包裹成[setMyState]时,这个数组是每次渲染都会生成的新引用,会导致组件每次渲染都触发useEffect回调——这只是一种强制触发的hack,绝非正确写法; - 移除
setMyState后,依赖数组只剩myGlobalContext,此时只要myGlobalContext变化,useEffect就会正常触发,这才是符合预期的正确逻辑。
正确做法
永远不要把useState的setter函数放进useEffect的依赖数组里,React官方明确保证:setter函数的引用在组件生命周期内是不变的,完全不需要作为依赖项添加。
你的正确代码应该是:
//... useEffect(() => { console.log("This code will trigger when myGlobalContext changes."); }, [myGlobalContext]); //..
至于你提到的“将依赖项用嵌套括号包裹”,这根本不是React的官方要求,只是一种会导致依赖数组每次渲染都变化的hack手段,完全不推荐使用——它会让useEffect在每次组件渲染时都执行,通常不是业务需要的逻辑,还可能引发性能问题。
内容的提问来源于stack exchange,提问作者josealvarez97
相关产品推荐
相关产品推荐

