ReactJS注册后跳转Dashboard失败 禁止返回注册页实现
问题描述
实现用户跳转至Dashboard页面后,禁止通过浏览器后退操作返回SignUp注册页的路由管控逻辑:已搭建SignUp注册页与Dashboard页面,预期逻辑为用户完成注册后自动跳转至Dashboard,且用户点击浏览器后退按钮时会被拦截,无法回到注册页。
但实际运行时注册流程执行完成后,URL不会变更为Dashboard路径,页面始终停留在SignUp注册页。
运行效果截图
问题代码
App.js
export const countContext = createContext(); const initialState = { basket: [], }; const reducer = (state, action) => { switch (action.type) { case 'ADD_TO_BASKET': return { ...state, basket: [...state.basket, action.item], }; default: return state; } }; function App() { const [basketCount, dispatch] = useReducer(reducer, initialState); return ( <> <Router> <AuthProvider> <countContext.Provider value={{ countState: basketCount, countDispatch: dispatch }} > <Routes> <Route exact path='/' element={ basketCount.length !== 1 ? ( <Navigate to='/SignUp' replace='true' /> ) : ( <Navigate to='/Dashboard' replace='true' /> ) } /> <Route exact path='/Dashboard' element={ basketCount.length !== 1 ? ( <Navigate to='/SignUp' replace='true' /> ) : ( <Navigate to='/Dashboard' replace='true' /> ) } /> <Route exact path='/' element={<SignUp />} /> <Route exact path='/Dashboard' element={<Dashboard />} /> <Route exact path='/SignUp' element={<SignUp />} /> </Routes> </countContext.Provider> </AuthProvider> </Router> </> ); }
SignUp.js
function SignUp() { const nameRef = useRef(); const countCountext = useContext(countContext); const mobileRef = useRef(); const otpRef = useRef(); const auth = getAuth(); const { signNumber } = useAuth(); const [error, setError] = useState(); const [loading, setLoading] = useState(false); const [visible, setVisible] = useState(true); const navigate = useNavigate(); async function handleSubmit(e) { e.preventDefault(); try { setError(''); setLoading(true); const code = otpRef.current.value; window.confirmationResult .confirm(code) .then((result) => { countCountext.countDispatch({ type: 'ADD_TO_BASKET', item: { name: nameRef.current.value, mobile: mobileRef.current.value, }, }); navigate('/Dashboard', { replace: true }); }) .catch((error) => { console.log(error.message); }); } catch { setError('Failed to Sign Up'); } setLoading(false); } function getOtp() { window.recaptchaVerifier = new RecaptchaVerifier( 'sign-in-button', { size: 'invisible', callback: (response) => { onSignInSubmit(); }, }, auth ); } async function onSignInSubmit(e) { e.preventDefault(); getOtp(); const phoneNumber = '+91' + mobileRef.current.value; const appVerifier = window.recaptchaVerifier; await signNumber(auth, phoneNumber, appVerifier); setLoading(false); } const notNull = (e) => { if (e.target.value.length === 10) { console.log(e.target.value.length); setVisible(false); } else { setVisible(true); setLoading(true); } }; return ( <div className='outer' autoComplete='off'> <Card className='center' autoComplete='off'> <Card.Body> <h2>Sign Up</h2> <Form onSubmit={handleSubmit} method='POST' autoComplete='off'> <div id='sign-in-button'></div> {error && <Alert variant='danger'>{error}</Alert>} <Form.Group id='name'> <Form.Label>Enter Your Name:</Form.Label> <Form.Control required ref={nameRef} autoComplete='off' ></Form.Control> </Form.Group> <Form.Group id='mobile-number'> <Form.Label>Enter Your Mobile Number:</Form.Label> <Form.Control autoComplete='off' type='number' name='mobile' required ref={mobileRef} onChange={(e) => notNull(e)} ></Form.Control> <br /> </Form.Group> <Button onClick={onSignInSubmit} disabled={visible}> Get OTP </Button> <br /> <Form.Group> <Form.Label disabled={visible}>Enter Your OTP:</Form.Label> <br /> <Form.Control autoComplete='off' disabled={visible} type='number' name='opt' required ref={otpRef} ></Form.Control> <br /> </Form.Group> <Button disabled={loading} type='submit'> SIGN IN </Button> </Form> </Card.Body> </Card> <div className='w-100 text-center mt-2'> <Link to='/Login'>Doctor Login</Link> </div> </div> ); }
问题原因
- 路由重复定义:React Router v6 中同一路径仅会匹配最先声明的Route,你为
/、/Dashboard重复定义了两组路由,实际渲染的始终是带重定向逻辑的路由,声明的<Dashboard />、<SignUp />页面路由完全不生效。 - 重定向逻辑死循环:
/Dashboard路径的重定向规则中,当basketCount.length === 1时配置的是重定向到/Dashboard自身,形成无限重定向,永远不会渲染Dashboard组件。 - 异步状态时序问题:React的状态更新是异步批量执行的,你dispatch修改basket后立刻调用navigate跳转,路由守卫判断时拿到的还是更新前的basket状态,会被错误重定向回SignUp页。
- 状态取值错误:
useReducer返回的basketCount是整个state对象,判断注册状态时应该取basketCount.basket.length,直接取basketCount.length永远为undefined,条件判断始终不成立。
修复方案
1. 修正App.js路由配置
删除重复路由,将权限守卫逻辑直接绑定到对应页面路由上:
function App() { const [basketCount, dispatch] = useReducer(reducer, initialState); // 正确取basket数组长度判断注册状态 const isRegistered = basketCount.basket.length === 1; return ( <> <Router> <AuthProvider> <countContext.Provider value={{ countState: basketCount, countDispatch: dispatch }} > <Routes> {/* 根路径自动跳转 */} <Route exact path='/' element={ isRegistered ? <Navigate to='/Dashboard' replace /> : <Navigate to='/SignUp' replace /> } /> {/* 注册页守卫:已注册用户访问直接跳Dashboard,阻止后退回注册页 */} <Route exact path='/SignUp' element={ isRegistered ? <Navigate to='/Dashboard' replace /> : <SignUp /> } /> {/* 仪表盘守卫:未注册用户访问跳回注册页 */} <Route exact path='/Dashboard' element={ isRegistered ? <Dashboard /> : <Navigate to='/SignUp' replace /> } /> </Routes> </countContext.Provider> </AuthProvider> </Router> </> ); }
2. 修正SignUp页提交逻辑
处理异步时序问题,统一loading状态管理:
async function handleSubmit(e) { e.preventDefault(); try { setError(''); setLoading(true); const code = otpRef.current.value; await window.confirmationResult.confirm(code) .then(async (result) => { countCountext.countDispatch({ type: 'ADD_TO_BASKET', item: { name: nameRef.current.value, mobile: mobileRef.current.value, }, }); // 等待状态更新完成再跳转,避免守卫拿到旧值 await new Promise(resolve => setTimeout(resolve, 0)); navigate('/Dashboard', { replace: true }); }) } catch(err) { setError('Failed to Sign Up'); console.log(err.message); } finally { // 统一在finally里重置loading,避免异常时loading状态卡死 setLoading(false); } }
修复后效果
- 用户完成注册后可正常跳转至Dashboard页面
- 已注册用户点击浏览器后退按钮时,会被SignUp路由的守卫规则自动重定向回Dashboard,无法返回注册页
- 未注册用户直接访问Dashboard地址会被拦截跳转回注册页,权限逻辑正常
内容的提问来源于stack exchange,提问作者Teja
相关产品推荐
相关产品推荐

