成功登录或注销重定向后更新导航栏

Update navbar after success login or logout redirection

我在 React 中有一个简单的身份验证应用程序,问题是当用户登录时,他被重定向到他的个人资料,但我的导航栏组件没有正确呈现。仅在我刷新页面后应用更改,而不是在请求直接重定向时应用更改。

应用程序组件:

function App() {

  const auth = useAuth();

  return (
    <>
      <Router>
        {auth.auth ? <Navbar/> : <NotAuthNavbar/>}
        <Routes>
            <Route path="/" element={<Dashboard/>}/>
            <Route path="/login" element={<Login/>}/>
            <Route path="/signup" element={<Register/>}/>
            <Route path="/profile"
                  element={
                    <PrivateRoute>
                      <Profile/>
                    </PrivateRoute>
                  }
            />
            <Route path="/confirm-register/:tokenConfirm" element={<ConfirmRegister/>}/>
            <Route path="/registered" element={<RegisterMessage/>}/>
        </Routes>
      </Router>
    </>
  );
}

export default App;

授权自定义挂钩:

const useAuth = () => {

    const [auth,setAuth] = useState(null);
    const [user,setUser] = useState({});

    const isAuth = async() => {
        await axios.get('http://localhost:5000/api/logged-user/',{withCredentials:true})
        .then(res => {
            setUser(res.data);
            setAuth(true);
        })
        .catch(error => {
            setAuth(false);
        });
    }


    useEffect(() => {
        isAuth();
    },[auth])

    return{
        auth:auth,
        user:user
    }
}
export default useAuth;

Navbar 和 NotAuthNavbar:

///Navbar.jsx
const Navbar = () => {

    const auth = useAuth();
    const navigate = useNavigate();

    const logout = async() =>{
        await axios.get("http://localhost:5000/api/logout/",{withCredentials:true})
        .then(res => {
            console.log(res.data);
            navigate('/login');
            return;
        })
    }

    return(
        <section>
            <div className="navbar">
                <ul className="navbar-menu">
                    <li><Link to={"/dashboard"}>Dashboard</Link></li>
                    <li><Link to={"/profile"}>Welcome {auth.user.username}</Link></li>
                    <li><button type='button' onClick={logout}>Logout</button></li>
                </ul>
            </div>
            <Outlet />
        </section>
        )
}

export default Navbar;

/// NotAuthNavbar.jsx
const NotAuthNavbar = () => {


    return(
        <section>
            <div className="navbar">
                <ul className="navbar-menu">
                    <li><Link to={"/dashboard"}>Dashboard</Link></li>
                    <li><Link to={"/signup"}>Sign Up</Link></li>
                    <li><Link to={"/login"}>Sign In</Link></li>
                </ul>
            </div>
            <Outlet />
        </section>
        )
}

export default NotAuthNavbar;

当用户登录时,我使用 Navigate by react-router-dom 重定向到他的个人资料。

我不知道如何在登录和注销后更新导航栏。我一直在想有一个我没有看到的小细节,但我已经被困在这里几个小时了。

提前致谢。

编辑

这是登录的功能

const login = async(e) => {

        e.preventDefault();

        await axios.post("http://localhost:5000/api/signin/",{
            email:email.email,
            password:password.password
        },{
            withCredentials:true,
        })
        .then(res => {
            setIsSubmitted(true);
            if(res.status === 200){
                alert('!LOGGED');
                navigate('/profile');
                return res.data;
            }
        })
        .catch(error => {
            let parsedErrors = [];
            parsedErrors = JSON.parse(error.request.response);

            setHandleErrors(parsedErrors);

            setIsSubmitted(true);
        })
    }

问题

React hooks 不共享状态。

解决方案

useAuth 状态移动到 React 上下文中并将其提供给应用程序。然后 useAuth 挂钩应该使用 useContext 挂钩来访问单一身份验证状态。

示例:

创建 AuthContext、提供程序和挂钩。

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

const AuthContext = createContext({
  auth: null,
  setAuth: () => {},
  user: null,
});

export useAuth = () => useContext(AuthContext);

const AuthProvider = ({ children }) => {
  const [auth, setAuth] = useState(null);
  const [user, setUser] = useState(null);

  useEffect(() => {
    const isAuth = async () => {
      try {
        const res await axios.get(
          'http://localhost:5000/api/logged-user/',
          { withCredentials: true }
        );
      
        setUser(res.data);
      } catch(error) {
        setUser(null);
      };
    };

    isAuth();
  }, [auth]);

  return (
    <AuthContext.Provider value={{ auth, setAuth, user }}>
      {children}
    </AuthContext.Provider>
  );
};

export default AuthProvider;

AuthProvider 组件包装 App 组件。

import AuthProvider from "../path/to/AuthContext";

...

<AuthProvider>
  <App />
</AuthProvider>

现在只有一个 authuser 状态 useAuth 挂钩都引用相同的状态。

应用程序

function App() {
  const { auth } = useAuth();

  return (
    <Router>
      {auth ? <Navbar /> : <NotAuthNavbar />}
      <Routes>
        <Route path="/" element={<Dashboard />} />
        <Route path="/login" element={<Login />} />
        <Route path="/signup" element={<Register />} />
        <Route
          path="/profile"
          element={(
            <PrivateRoute>
              <Profile />
            </PrivateRoute>
          )}
        />
        <Route path="/confirm-register/:tokenConfirm" element={<ConfirmRegister />} />
        <Route path="/registered" element={<RegisterMessage />} />
      </Routes>
    </Router>
  );
}

导航栏

访问 setAuth 更新程序功能以在成功注销后设置 auth false。

const Navbar = () => {
  const { setAuth, user } = useAuth();
  const navigate = useNavigate();

  const logout = async () => {
    const res await axios.get(
      "http://localhost:5000/api/logout/",
      { withCredentials: true }
    );
    console.log(res.data);
    setAuth(false);
    navigate('/login');
  }

  return(
    <section>
      <div className="navbar">
        <ul className="navbar-menu">
          <li><Link to={"/dashboard"}>Dashboard</Link></li>
          <li><Link to={"/profile"}>Welcome {user.username}</Link></li>
          <li><button type='button' onClick={logout}>Logout</button></li>
        </ul>
      </div>
      <Outlet />
    </section>
  );
};

组件使用 login 函数

使用 useAuth 挂钩访问 setAuth 更新程序功能,并在成功验证后设置 auth true。

const { setAuth } = useAuth();

...

const login = async (e) => {
  e.preventDefault();

  try {
    const res = await axios.post(
      "http://localhost:5000/api/signin/",
      {
        email: email.email,
        password: password.password
      },
      {
        withCredentials: true,
      }
    );

    if (res.status === 200) {
      alert('!LOGGED');
      setAuth(true);
      navigate('/profile');
    }
  } catch(error) {
    setHandleErrors(JSON.parse(error.request.response));
  } finally {
    setIsSubmitted(true);
  }
}

注意:将 async/await 与 Promise 链混合使用是 anti-pattern。一般来说,你应该 select 一个或另一个。我使用 async/awaittry/catch 来处理被拒绝的承诺和其他可能发生的错误。