useEffect依赖react-navigation的useIsFocused时setState不生效
问题根因
两个核心错误导致状态不更新:
- 违反React状态不可变更新原则:代码里
const newRoute = route;仅复制了状态对象的引用地址,没有创建全新的对象/数组副本,后续直接修改newRoute.visitas本质是在直接修改原state。最终调用setRoute(newRoute)时,React对state做浅比较发现传入的引用和当前state的引用完全一致,会判定状态没有变化,跳过重渲染和对应useEffect的触发。这也是为什么setIsLoading(true)能正常执行——它在副作用函数运行时就被直接调用,不需要等状态变更检测,但监听route的副作用里的setIsLoading(false)因为状态更新被跳过永远不会执行,最终页面卡在加载态。 - 副作用依赖缺失:监听
isFocused的useEffect内部使用了route、myRoute.params等外部值,但依赖数组仅传入了isFocused,闭包会长期缓存首次渲染时的空route对象,不仅会导致访问newRoute.visitas时抛出异常,也会导致状态更新逻辑拿到的值始终是过期的旧值。
修复方案
- 所有状态更新必须做不可变拷贝,嵌套对象/数组要逐层生成新的引用
- 补全useEffect的依赖项,或者使用函数式更新拿到最新的state值,避免闭包过期问题
- 页面聚焦触发更新的逻辑优先使用
useFocusEffect实现,比useIsFocused配合普通useEffect的写法更符合react-navigation的设计规范
修复后的核心代码:
import React, { useEffect, useState, useCallback } from 'react'; import { useFocusEffect } from '@react-navigation/native'; const MyRoute = ({ navigation, route: myRoute }) => { const [route, setRoute] = useState({}); const [isLoading, setIsLoading] = useState(true); useEffect(() => { console.log('route call') if (Object.keys(route).length) { setIsLoading(false); } }, [route]); useEffect(() => { console.log(isLoading); }, [isLoading]); // 用useFocusEffect替代监听isFocused的普通useEffect useFocusEffect( useCallback(() => { if (myRoute.params?.isChecked) { setIsLoading(true); // 函数式更新拿到最新的route状态,同时做逐层拷贝生成新引用 setRoute(oldRoute => { const newRoute = { ...oldRoute, visitas: oldRoute.visitas.map(item => { if (item.sub_id === myRoute.params.sub_id) { return { ...item, realizada: true, check_in_out: myRoute.params.check_in_out } } return item; }) }; return newRoute; }); } }, [myRoute.params]) ); /** ... */ }
注意:不要用
JSON.parse(JSON.stringify(route))这种暴力深拷贝的方式处理状态,如果对象里有函数、undefined、Date等类型值会出现异常,逐层展开拷贝或者用Immer这类不可变更新库更稳妥。
内容的提问来源于stack exchange,提问作者leonardogbxv
相关产品推荐
相关产品推荐

