React自定义useChanges钩子补全useEffect依赖无限循环,如何符合ESLint规范?
问题根因
你遇到的无限循环是因为getChangedItemAndOriginalItem和applyChanges两个函数每次组件渲染时都会生成新的引用,加入useEffect依赖数组后,每次函数引用变化就会触发useEffect执行,执行逻辑又导致组件重渲染生成新的函数,陷入死循环。
方案1:补全自定义hook内部的useCallback依赖
首先在useChanges自定义hook内部,将两个返回的函数用useCallback包裹,同时把函数内部用到的所有外部变量都补全到useCallback的依赖数组中,确保依赖无变化时函数引用不会更新:
// useChanges 内部实现示例 import { useCallback } from 'react' function useChanges(initialChanges, originalItems, selector) { // 其他内部逻辑... const getChangedItemAndOriginalItem = useCallback(() => { // 原有函数逻辑 }, [originalItems, selector]) // 补全所有内部用到的外部变量 const applyChanges = useCallback((newValues) => { // 原有函数逻辑 }, [getChangedItemAndOriginalItem]) // 依赖的内部函数也要加入 return { getChangedItemAndOriginalItem, applyChanges } }
如果作为参数传入的selector函数是在父组件内定义的,也需要用useCallback包裹,避免每次渲染生成新引用导致hook内部的useCallback重新生成函数。
方案2:用useRef存储函数引用规避依赖校验
如果不想改动自定义hook的内部逻辑,可以在组件内用useRef保存函数的最新引用,业务useEffect只需要依赖debouncedValues即可:
// 组件内使用示例 const { applyChanges } = useChanges(/* 你的入参 */) const applyChangesRef = useRef(applyChanges) // 同步最新的函数引用 useEffect(() => { applyChangesRef.current = applyChanges }, [applyChanges]) // 业务逻辑useEffect useEffect(() => { applyChangesRef.current(debouncedValues) }, [debouncedValues])
该写法逻辑正确,且完全符合ESLint的依赖校验规则。
方案3:使用useEvent(React 18+ 官方解决方案)
如果使用React 18及以上版本,可以直接用useEvent(目前为实验性API,后续会正式纳入稳定版)包裹不需要响应式更新的函数,它会始终返回同一个引用,不需要加入useEffect依赖数组:
// useChanges 内部实现示例 import { useEvent } from 'react' function useChanges(initialChanges, originalItems, selector) { // 其他内部逻辑... const applyChanges = useEvent((newValues) => { // 原有函数逻辑 }) }
这个API就是为了解决「事件类函数不需要加入useEffect依赖」的场景设计的,完全符合官方规范和ESLint校验规则。
内容的提问来源于stack exchange,提问作者Martin Hofhanzl
相关产品推荐
相关产品推荐

