子组件更新Context后如何通过router.push()实现路由跳转?
如何在React Context状态更新后执行路由跳转(避免首次渲染触发)
问题场景
我有一个Context Provider用于管理列表状态,子组件通过点击按钮更新该状态后需要跳转到/another页面(必须确保状态更新完成后再跳转)。但当前使用useEffect监听myList变化时,会在组件首次渲染时就触发路由跳转,不符合预期。
Context Provider代码
// Context Provider import { createContext, useContext, useState } from 'react'; export let MyContext = createContext(); let initList = ['1']; // initial state export default function MyProvider({ children }) { const [myList, setMyList] = useState(initList); const context = { myList, setMyList }; return ( <MyContext.Provider value={context}> {children} </MyContext.Provider> ); }
子组件代码(存在问题)
// Child Component 'use client'; import { useRouter } from 'next/navigation'; import { useContext, useEffect, useState } from 'react'; import { MyContext } from '../context-provider'; export default function Child() { let { myList, setMyList } = useContext(MyContext); const router = useRouter(); console.log(myList); useEffect(() => { router.push('/another'); }, [myList, router]); return ( <div> <button type='button' onClick={() => setMyList([...myList, '2'])}> Update state and Navigate </button> </div> ); }
我目前想到的方案是比较myList更新前后的长度,但想知道有没有其他更优雅的实现方式?
解决方案
方案1:添加触发标记,区分首次渲染与用户操作
在子组件中新增一个状态标记是否是用户点击触发的更新,仅当该标记为true且状态更新后才执行跳转:
// Child Component 'use client'; import { useRouter } from 'next/navigation'; import { useContext, useEffect, useState } from 'react'; import { MyContext } from '../context-provider'; export default function Child() { let { myList, setMyList } = useContext(MyContext); const router = useRouter(); const [isTriggeredByClick, setIsTriggeredByClick] = useState(false); useEffect(() => { if (isTriggeredByClick) { router.push('/another'); // 重置标记,避免后续状态更新重复触发跳转 setIsTriggeredByClick(false); } }, [myList, router, isTriggeredByClick]); return ( <div> <button type='button' onClick={() => { setMyList(prev => [...prev, '2']); // 使用函数式更新确保取到最新状态 setIsTriggeredByClick(true); }} > Update state and Navigate </button> </div> ); }
方案2:使用useRef记录初始状态,跳过首次渲染
用useRef存储初始列表状态,在useEffect中判断当前状态是否与初始状态一致,只有不一致时才执行跳转:
// Child Component 'use client'; import { useRouter } from 'next/navigation'; import { useContext, useEffect, useRef } from 'react'; import { MyContext } from '../context-provider'; export default function Child() { let { myList, setMyList } = useContext(MyContext); const router = useRouter(); const initialListRef = useRef(myList); useEffect(() => { // 仅当状态与初始状态不同时才跳转 if (myList !== initialListRef.current) { router.push('/another'); } }, [myList, router]); return ( <div> <button type='button' onClick={() => setMyList(prev => [...prev, '2'])} > Update state and Navigate </button> </div> ); }
方案3:在点击事件的状态更新回调中标记跳转
利用函数式更新的特性,在更新状态的同时标记需要跳转,确保状态更新完成后触发路由:
// Child Component 'use client'; import { useRouter } from 'next/navigation'; import { useContext, useEffect, useState } from 'react'; import { MyContext } from '../context-provider'; export default function Child() { let { myList, setMyList } = useContext(MyContext); const router = useRouter(); const [shouldNavigate, setShouldNavigate] = useState(false); useEffect(() => { if (shouldNavigate) { router.push('/another'); setShouldNavigate(false); } }, [shouldNavigate, router]); return ( <div> <button type='button' onClick={() => { setMyList(prev => { // 更新状态后标记需要跳转 setShouldNavigate(true); return [...prev, '2']; }); }} > Update state and Navigate </button> </div> ); }
内容的提问来源于stack exchange,提问作者shivam
相关产品推荐
相关产品推荐

