React useEffect依赖警告之谜:两种写法的核心差异解析
两种React useEffect写法的差异与警告原因解析
背景情况
我有两个组件都作为Home组件的子组件:一个是展示数据的当前组件,另一个是月份日历组件。用户在日历里选月份时,会触发当前组件发起新的API请求。其中选项2运行完全正常,没任何错误警告,但用选项1时会弹出React Hook的依赖警告:React Hook useEffect has a missing dependency: 'getComponentData'. Either include it or remove the dependency array.
状态定义
const [componentData, setComponentData] = useState([]);
选项1代码
useEffect(() => { getComponentData(props.calendarInput); }, [props.calendarInput]); async function getComponentData(sm) { const data = await api.getUtilityServicePays(sm); setComponentData(data); }
选项2代码
useEffect(() => { async function getComponentData() { const data = await api.getUtilityServicePays(props.calendarInput); setComponentData(data); } getComponentData(); }, [props.calendarInput]);
核心差异
1. 函数定义的位置不一样
- 选项1里的
getComponentData是定义在组件顶层的,每次组件重新渲染(比如props或state变化触发重渲染),这个函数都会被重新创建一次,生成新的引用。 - 选项2的
getComponentData是写在useEffect内部的,只有当useEffect的依赖数组(也就是props.calendarInput)变化时,这个函数才会被重新创建。
2. React对依赖的追踪范围不同
- 选项1中,
useEffect内部调用了顶层的getComponentData函数,按照React Hook的规则,useEffect的依赖数组必须包含所有在它内部用到的外部变量或函数。但这里只加了props.calendarInput,没加getComponentData,所以lint工具会抛出警告。 - 选项2里的函数是
useEffect自己内部的,它的作用域被限制在useEffect里面,React不需要把它加到依赖数组里——毕竟它不会被外部引用,而且每次useEffect执行时都会重新生成,不会有“过期函数”的问题。
为什么选项2不会触发警告?
选项2里的内部函数直接引用了props.calendarInput,而这个值已经被加到了useEffect的依赖数组里,React能正确追踪到它的变化。
反观选项1,顶层的getComponentData每次渲染都会变,useEffect调用了它却没把它放进依赖数组,React的lint规则会严格检查这种外部引用,哪怕这个函数里只用到了传入的参数和稳定的setComponentData(setComponentData是React提供的稳定引用,不需要加进依赖),还是会判定这是遗漏的依赖,从而弹出警告。
如果要修复选项1的警告,可以用useCallback把getComponentData包裹起来,让它的引用在依赖不变时保持稳定,再把它加入useEffect的依赖数组:
const getComponentData = useCallback(async (sm) => { const data = await api.getUtilityServicePays(sm); setComponentData(data); }, []); // 这里依赖为空,因为只用到了稳定的setComponentData和api useEffect(() => { getComponentData(props.calendarInput); }, [props.calendarInput, getComponentData]);
内容的提问来源于stack exchange,提问作者Sam Saarian
相关产品推荐
相关产品推荐

