Redux Toolkit 场景下如何实现页面导航拦截功能?
方案适配性判断
首先明确你提到的三个方案的适用场景:
usePrefetch完全不适用,该钩子是RTK Query用于提前拉取接口数据做缓存的能力,和导航拦截没有任何关联,直接排除- 手动封装
history.push等跳转方法的方案仅能覆盖你主动调用封装方法的跳转场景,无法拦截浏览器前进后退、Link组件点击、页面刷新等行为,维护成本高,不推荐 - 最优方案是React Router官方导航拦截能力 + Redux Toolkit状态读取,逻辑解耦,能覆盖所有跳转场景,实现成本最低
实现步骤
首先你需要在Redux Toolkit的对应slice中暴露队列长度的选择器,方便组件读取状态:
// queueSlice.js 示例 import { createSlice } from '@reduxjs/toolkit' const initialState = { taskList: [] // 你的队列存储字段 } const queueSlice = createSlice({ name: 'queue', initialState, reducers: { // 你的队列增删改逻辑 } }) // 导出队列长度选择器 export const selectQueueLength = state => state.queue.taskList.length export default queueSlice.reducer
如果你使用React Router v5
直接用内置的Prompt组件配合Redux状态即可:
import { Prompt } from 'react-router-dom' import { useSelector } from 'react-redux' import { selectQueueLength } from './queueSlice' function YourBusinessPage() { const queueLength = useSelector(selectQueueLength) const needBlockNavigation = queueLength > 0 return ( <> {/* 直接硬拦截,不允许跳转,不需要用户确认 */} <Prompt when={needBlockNavigation} message={() => false} /> {/* 如果需要弹出确认框让用户选择,替换为下面的写法 */} {/* <Prompt when={needBlockNavigation} message="当前队列还有未处理任务,确定要离开页面吗?" /> */} {/* 页面其他业务内容 */} </> ) }
如果你使用React Router v6
v6版本移除了Prompt组件,使用官方提供的useBlocker钩子实现:
import { useBlocker } from 'react-router-dom' import { useSelector } from 'react-redux' import { selectQueueLength } from './queueSlice' function YourBusinessPage() { const queueLength = useSelector(selectQueueLength) const needBlockNavigation = queueLength > 0 // 硬拦截:直接禁止所有跳转 useBlocker(() => needBlockNavigation) /* 如果需要弹出自定义确认框,替换为下面的逻辑 const blocker = useBlocker(() => needBlockNavigation) useEffect(() => { if (blocker.state === 'blocked') { const isConfirmed = window.confirm('当前队列还有未处理任务,确定要离开页面吗?') isConfirmed ? blocker.proceed() : blocker.reset() } }, [blocker]) */ // 页面其他业务内容 return <></> }
额外场景:拦截页面刷新/关闭
如果需要拦截浏览器刷新、关闭标签页的行为,需要额外监听beforeunload事件:
useEffect(() => { const handleBeforeUnload = (e) => { if (queueLength > 0) { e.preventDefault() e.returnValue = '' } } window.addEventListener('beforeunload', handleBeforeUnload) return () => window.removeEventListener('beforeunload', handleBeforeUnload) }, [queueLength])
全局拦截方案
如果需要对所有页面生效该拦截规则,只需要把上述逻辑放在全局Layout组件中即可,不需要每个页面单独引入。
内容的提问来源于stack exchange,提问作者data one
相关产品推荐
相关产品推荐

