You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 12:36:20