反应私人路线的麻烦

Trouble with react private routes

我正在尝试创建受保护的路线。我有一个反应上下文 returns true or false 如果用户是否登录。

问题是,即使我通过了身份验证,我仍然无法访问仪表板路由(即使我已登录,它也会重定向到登录)。我认为这是因为我向我的服务器发送了一个 api 请求以检查用户是否已通过身份验证,并且当时上下文值为 undefined

我试图通过在渲染路由之前检查上下文是否未定义来解决这个问题,但它仍然不起作用。

App.js

const ctx = useContext(userContext);
  console.log(ctx.authed); // return true or false

  return (
    <div className='App'>
      <Router>
        <Switch>
          <Route path='/' exact component={HomePage}></Route>
          <Route exact path='/auth/register' component={Register}></Route>
          <Route exact path='/auth/login' component={Login}></Route>

          {!ctx.loading ? (
            <ProtectedRoute
              path='/dashboard'
              isAuth={ctx.authed}
              exact
              component={Dashboard}
            />
          ) : null}
        </Switch>
      </Router>
    </div>
  );

ProtectedRoute.js

function ProtectedRoute({ isAuth, component: Component, ...rest }) {
  return (
    <Route
      {...rest}
      render={(props) => {
        if (isAuth) {
          return <Component />;
        } else {
          return (
            <Redirect to={{ pathname: '/login', 
            state: { from: props.location } }} />
          );
        }
      }}
    />
  );
}

我的上下文如下:

export const userContext = createContext({});
export default function Context(props) {
  const [authed, setAuthed] = useState(undefined);
  const [loading, setLoading] = useState(false);

  const fetchData = async () => {
    setLoading(true);
    await axios({
      method: 'GET',
      withCredentials: true,
      credentials: 'include',
      url: '/checkAuthentication',
    }).then((res) => {
      setAuthed(res.data.authenticated);
      setLoading(false);
    });
  };

  useEffect(() => {
    fetchData();
  }, []);

  return (
    <userContext.Provider value={{ loading, authed }}>
      {props.children}
    </userContext.Provider>
  );
}

我已经坚持了一段时间,如果有任何帮助,我将不胜感激。谢谢!

您可以使用 useEffect 钩子来查明 React 何时更改了您的 authed 变量。当它发生时,您可以检查用户是否已登录。如果没有,您可以使用 history.push('/login')。您可以从 react-router-domuseHistory 钩子中得到 history。在您的登录页面中,您可以使用上下文的加载变量来显示任何加载组件。

使用这种方法,您不需要使用难以管理和降低代码可读性的冗余组件。

你的代码的问题是组件挂载时context的loading变量总是false,此时authed的值为undefined。这就是为什么 <ProtectedRoute /> 总是呈现并将您重定向到登录页面。

@Alyks 解决方案帮助了我。除了添加 useEffect 块外,没有任何变化。

  const ctx = useContext(myContext);
  const [isAuth, setIsAuth] = useState(ctx.authed);

  useEffect(() => {             //
    if (ctx.authed) {           //
      setIsAuth(ctx.authed);   <-- added this
    }                           //
  }, [ctx.authed]);             // 

  return (
    <div className='App'>
      <Router>
        <a href='/dashboard'>Dashboard</a>
        <a href='/auth/login'>Login</a>
        <Switch>
          <Route path='/' exact component={HomePage}></Route>
          <Route exact path='/auth/register' component={Register}></Route>
          <Route exact path='/auth/login' component={Login}></Route>

          {!ctx.loading ? (
            <ProtectedRoute
              path='/dashboard'
              isAuth={isAuth}
              exact
              component={Dashboard}
            />
          ) : null}
        </Switch>
      </Router>
    </div>
  );