React useEffect依赖缺失警告咨询:功能正常为何仍报错?
问题场景
开发的应用包含6个针对不同API端点的fetch函数,定义在useEffect代码上方。当前逻辑为:首次加载时执行所有请求,仅当用户切换数据源(chosenSource)或类型(chosenType)时重新请求,功能符合预期,但终端出现了依赖缺失的警告。
代码示例
useEffect(() => { fetchType() fetchSource() fetchSet() }, [chosenType, chosenSource]) useEffect(() => { setIsLoading(true) fetchUsers() fetchUserProblems() fetchAuthority() },[])
警告信息(中文翻译)
第111行第8列:React Hook useEffect缺少依赖项:'fetchData'、'fetchSource'和'fetchType'。请将它们加入依赖数组,或者移除依赖数组。
react-hooks/exhaustive-deps第118行第7列:React Hook useEffect缺少依赖项:'fetchProblems'、'fetchAuthority'、'fetchUsers'和'setIsLoading'。请将它们加入依赖数组,或者移除依赖数组。
react-hooks/exhaustive-deps
警告原因
React的exhaustive-deps ESLint规则会严格检查useEffect回调中用到的所有外部变量和函数,要求必须将它们加入依赖数组。这是为了避免过时闭包问题:如果回调依赖的变量/函数发生了变化,但useEffect没更新,就会导致回调里使用的还是旧版本的变量/函数,引发逻辑错误。
你的fetch函数和setIsLoading(如果是组件内定义的函数)每次组件渲染时都会重新创建一个新的函数引用,ESLint会识别到这些引用在每次渲染时都不同,因此判定它们是需要追踪的依赖项。
解决方法
方法1:用useCallback缓存函数(推荐)
将每个fetch函数用useCallback包裹,缓存函数引用,只有当它依赖的外部变量变化时才重新创建函数。之后把缓存后的函数加入useEffect的依赖数组即可:
// 缓存fetch函数 const fetchType = useCallback(async () => { // 你的请求逻辑 }, [/* 这里填入fetchType用到的所有外部依赖,比如API地址、axios实例等 */]) const fetchSource = useCallback(async () => { // 你的请求逻辑 }, [/* 对应的依赖 */]) const fetchSet = useCallback(async () => { // 你的请求逻辑 }, [/* 对应的依赖 */]) const fetchUsers = useCallback(async () => { // 你的请求逻辑 }, [/* 对应的依赖 */]) const fetchUserProblems = useCallback(async () => { // 你的请求逻辑 }, [/* 对应的依赖 */]) const fetchAuthority = useCallback(async () => { // 你的请求逻辑 }, [/* 对应的依赖 */]) // 更新后的useEffect useEffect(() => { fetchType() fetchSource() fetchSet() }, [chosenType, chosenSource, fetchType, fetchSource, fetchSet]) useEffect(() => { setIsLoading(true) fetchUsers() fetchUserProblems() fetchAuthority() },[fetchUsers, fetchUserProblems, fetchAuthority, setIsLoading])
方法2:临时禁用ESLint规则(不推荐)
如果你确定这些fetch函数不会因为外部依赖变化而失效,可以在useEffect上方添加注释禁用该规则,但这种做法会掩盖潜在的过时闭包问题,后续维护时容易出bug:
// eslint-disable-next-line react-hooks/exhaustive-deps useEffect(() => { fetchType() fetchSource() fetchSet() }, [chosenType, chosenSource]) // eslint-disable-next-line react-hooks/exhaustive-deps useEffect(() => { setIsLoading(true) fetchUsers() fetchUserProblems() fetchAuthority() },[])
方法3:将函数移到useEffect内部
如果某个fetch函数只在对应的useEffect中使用,可以直接把函数定义写在回调内部,这样就不需要添加依赖,但会导致代码重复(如果多个地方用到同一个fetch函数):
useEffect(() => { const fetchType = async () => { // 请求逻辑 } const fetchSource = async () => { // 请求逻辑 } const fetchSet = async () => { // 请求逻辑 } fetchType() fetchSource() fetchSet() }, [chosenType, chosenSource])
内容的提问来源于stack exchange,提问作者user3352042

