成功登录或注销重定向后更新导航栏
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>
现在只有一个 auth
和 user
状态 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/await
和 try/catch
来处理被拒绝的承诺和其他可能发生的错误。
我在 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>
现在只有一个 auth
和 user
状态 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/await
和 try/catch
来处理被拒绝的承诺和其他可能发生的错误。