React页面自导航触发无限循环及同页面不同URL参数导航的useEffect执行异常问题排查
解决React页面自导航时useEffect执行异常的问题
咱来一步步拆解这两个场景里的坑,以及对应的解决办法,帮你搞定useEffect执行不符合预期的问题。
场景1:useEffect无限执行的原因与修复
先看你这段代码:
const queryValues = queryString.parse(search); const { trip } = queryValues; useEffect(() => { console.log('Calling infinite times'); },[trip]);
为啥会无限循环?
主要有两种常见情况:
trip是引用类型(对象/数组):queryString.parse每次调用都会返回一个全新的对象,哪怕trip的内容和之前一模一样,它的内存地址也是新的。React对useEffect依赖项的比较是浅比较,所以每次组件渲染都会认为trip变了,触发useEffect执行。如果effect内部又不小心触发了组件重新渲染(比如修改了状态、或者再次更新URL参数),那可不就无限循环了嘛。- effect内部偷偷修改了URL参数:要是你在useEffect里调用了
history.push或history.replace来更新trip参数,那会直接导致search变化,组件重新渲染,trip更新,再次触发effect,形成死循环。
怎么修复?
- 如果
trip是字符串、数字这类基本类型:先检查effect内部有没有不必要的URL更新逻辑,确保只有当trip和当前URL里的参数真的不一样时,才去更新URL,避免触发重复渲染:useEffect(() => { // 先拿到当前URL里的trip参数 const currentTrip = queryString.parse(search).trip; if (currentTrip !== trip) { // 这里再执行更新URL的操作 } }, [trip, search]); - 如果
trip是对象/数组这类引用类型:用useMemo缓存解析后的结果,确保只有当search变化时才重新解析,同时避免每次渲染都生成新的引用:
要是你需要深比较const queryValues = useMemo(() => queryString.parse(search), [search]); const { trip } = queryValues; useEffect(() => { console.log('Only called when trip actually changes'); }, [trip]);trip的内容(比如对象内部属性变化才触发),可以用JSON.stringify(trip)作为依赖(注意小数据量场景用,大数据量会影响性能),或者用专门的第三方钩子比如use-deep-compare-effect。
场景2:URL参数更新后useEffect完全不执行的原因与修复
再看场景2的代码:
useEffect(() => { const queryValues = queryString.parse(search); const { trip } = queryValues; console.log('Calling infinite times'); },[]);
为啥参数更新了effect不跑?
因为你给useEffect传了空数组[]作为依赖,这意味着这个effect只会在组件首次挂载时执行一次,之后哪怕组件因为URL参数变化重新渲染,依赖项数组里没有任何变化,React就不会再触发这个effect了。你在effect内部解析search,但search变化时effect根本不会重新执行,自然就响应不了参数更新。
怎么修复?
把search添加到依赖项数组里,这样每当URL参数变化导致search更新时,effect就会重新执行:
useEffect(() => { const queryValues = queryString.parse(search); const { trip } = queryValues; console.log('Called when search changes'); }, [search]); // 把search加入依赖
如果你只关心trip的变化,也可以像场景1那样先缓存解析后的trip,然后把trip作为依赖,这样只会在trip真的变化时才触发effect。
内容的提问来源于stack exchange,提问作者Abhijit Desai
相关产品推荐
相关产品推荐

