开发者问题收集

在获取用户之前对路由器做出反应

2021-03-28
604

我有一个 MERN 应用程序,它使用仅 http cookies 和 jwt 作为身份验证。我在 React 中创建了一个提供程序来保存用户信息。前端也有受保护的路由。在后端,我有一个路由,如果存在 cookie,则返回当前用户,否则我将用户设置为 null。当应用程序启动、刷新或用户更改时,我会进行此调用。一切都按预期工作。

当我刷新页面或使用 URL 栏手动导航到某个页面时,就会出现问题。因为在这两种情况下,应用程序都会刷新,所以在提取完成之前,用户会在很短的时间内不存在。因此,我总是被重定向到主页,而不是我刷新或输入的页面。我尝试设置“正在加载”状态,但它只适用于应用程序组件。

示例:我在“/details/1234”上,我单击刷新,然后我被送回“/”。

有什么方法可以修复此行为吗?也许路由器中有一些变化或强制 React 在获取完成之前不执行任何操作?提前致谢。

这是我的提供程序的代码:

import { createContext, useEffect, useState } from 'react';

export const AuthContext = createContext();

function AuthContextProvider(props) {

    const [user, setUser] = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        async function initialUser() {
            try {
                const response = await fetch(`/api/users/profile`, {
                    headers: {
                        "Content-Type": "application/json"
                    },
                    method: "GET",
                    credentials: 'include'
                });
                setLoading(false);
                const userData = await response.json();
                return userData.message ? setUser(null) : setUser(userData)
            } catch (error) {
                console.log(error);
            }
        }
        if (!user) {
            initialUser();
        }
    }, [user]);

    return (
        <AuthContext.Provider value={{ user, loading, setUser }}>
            {props.children}
        </AuthContext.Provider>
    )
}

export default AuthContextProvider

这是 App 组件的代码。App 组件由提供程序和路由器在 index.js 中包装

import { useContext } from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';
import './App.css';
import { AuthContext } from './context/AuthContext';
import Navigation from './components/Navigation/Navigation';
import Footer from './components/Footer/Footer';
import Homescreen from './components/HomeScreen/HomeScreen';
import LandingScreen from './components/LandingScreen/LandingScreen';
import SignScreen from './components/SignScreen/SignScreen';
import Search from './components/Search/Search';
import Genres from './components/Genres/Genres';
import Details from './components/Details/Details';
import Watch from './components/Watch/Watch';
import Profile from './components/Profile/Profile';


function App() {

  const { user, loading } = useContext(AuthContext);

  return (
    !loading
      ? <div className="App">
        <Navigation user={user} />
        <Switch>
          <Route exact path="/" render={() => (!user ? <LandingScreen /> : <Homescreen />)} />
          <Route path="/sign-in" render={() => (!user ? <SignScreen /> : <Redirect to="/" />)} />
          <Route path="/sign-up" render={() => (!user ? <SignScreen /> : <Redirect to="/" />)} />
          <Route path="/profile" render={() => (!user ? <Redirect to="/sign-in" /> : <Profile />)} />
          <Route path="/search" render={() => (!user ? <Redirect to="/sign-in" /> : <Search />)} />
          <Route path="/movies/:genre" render={() => (!user ? <Redirect to="/sign-in" /> : <Genres />)} />
          <Route path="/details/:id" render={() => (!user ? <Redirect to="/sign-in" /> : <Details />)} />
          <Route path="/watch/:id" render={() => (!user ? <Redirect to="/sign-in" /> : <Watch />)} />
        </Switch>
        <Footer />
      </div>
      : null
  );
}

export default App;
1个回答

您正在更新 loading 状态为 false, 更新 user 状态之前。您应该先更新用户数据, 然后 更新加载状态。当您在异步 JSON 响应处理之前更新 loading 状态时,它们之间将至少有 1 个渲染周期,从而允许路由器在 user 状态尚未确定的情况下渲染路由。我建议使用 try/catchfinally 块,这样无论如何,当提取处理完成时,加载状态都会更新。

function AuthContextProvider(props) {
    const [user, setUser] = useState(null);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        async function initialUser() {
            try {
                const response = await fetch(`/api/users/profile`, {
                    headers: {
                        "Content-Type": "application/json"
                    },
                    method: "GET",
                    credentials: 'include'
                });
                
                const userData = await response.json();
                userData.message ? setUser(null) : setUser(userData)
            } catch (error) {
                console.log(error);
            } finally {
                setLoading(false);
            }
        }
        if (!user) {
            initialUser();
        }
    }, [user]);

    return (
        <AuthContext.Provider value={{ user, loading, setUser }}>
            {props.children}
        </AuthContext.Provider>
    )
}
Drew Reese
2021-03-29