如何避免React中useEffect内router.push引发的无限循环?
解决React中因状态数组更新导致的路由push无限循环问题
问题根源
你当前的代码里,useEffect依赖了整个users数组——每次添加用户时都会生成新的数组引用,触发useEffect执行push修改URL。而push操作可能触发组件重新渲染,此时users的新数组引用又会再次触发useEffect,形成无限循环。实际上你只需要监听用户数量的变化,而非整个数组的引用变化。
方案1:修改useEffect依赖为用户数量
将useEffect的依赖从users改为users.length,length是基本类型,不会因为数组引用变化触发不必要的副作用,只有数量真的改变时才更新URL:
export default function Home() { const {push} = useRouter(); const [users, setUsers] = useState<string[]>([]); const addUser = useCallback(() => { setUsers([...users, `Tommy-${Math.random()}`]); }, [users]); useEffect(() => { push({ pathname: "/", query: { amount: users.length, }, }); }, [users.length, push]); // 仅监听用户数量变化 return ( <> <div> <button onClick={addUser}>Add user</button> <ul> {users.map((user) => ( <li key={user}>{user}</li> ))} </ul> </div> </> ); }
方案2:优化状态更新逻辑(更优)
使用useState的函数式更新修改users,让addUser不需要依赖users,避免每次用户变化时生成新的函数引用,同时保持监听用户数量:
export default function Home() { const {push} = useRouter(); const [users, setUsers] = useState<string[]>([]); // 函数式更新,无需依赖users const addUser = useCallback(() => { setUsers(prevUsers => [...prevUsers, `Tommy-${Math.random()}`]); }, []); useEffect(() => { push({ pathname: "/", query: { amount: users.length, }, }); }, [users.length, push]); return ( <> <div> <button onClick={addUser}>Add user</button> <ul> {users.map((user) => ( <li key={user}>{user}</li> ))} </ul> </div> </> ); }
方案3:增加URL状态判断(可选)
为了避免重复执行push(比如URL的amount已经和当前用户数量一致时),可以先判断当前URL参数,再决定是否更新:
import { useRouter } from 'next/router'; import { useState, useCallback, useEffect } from 'react'; export default function Home() { const router = useRouter(); const {push} = router; const [users, setUsers] = useState<string[]>([]); const addUser = useCallback(() => { setUsers(prevUsers => [...prevUsers, `Tommy-${Math.random()}`]); }, []); useEffect(() => { const currentAmount = router.query.amount; // 仅当URL参数与当前用户数量不一致时才更新 if (currentAmount !== String(users.length)) { push({ pathname: "/", query: { amount: users.length, }, }); } }, [users.length, push, router.query.amount]); return ( <> <div> <button onClick={addUser}>Add user</button> <ul> {users.map((user) => ( <li key={user}>{user}</li> ))} </ul> </div> </> ); }
内容的提问来源于stack exchange,提问作者Mimoid
相关产品推荐
相关产品推荐

