React:必须单击两次表单提交才能使用 useContext 全局设置上下文
React: Form submit must be clicked twice to set context globally using useContext
这里是 React 初学者。我正在使用 jwt、axios 和 useContext 在 React 中构建登录表单。从后端获得授权后,我使用 AuthProvider 将数据存储在全局上下文中并重定向到主页。主页首先检查授权并导航到未经授权的访问登录。但是,即使在登录时更新了 auth (useState),我在第一次点击时仍然会得到一个错误的值,即使经过授权也会被发送回登录。我到处都试过 useEffects 但无济于事。下面的代码
AuthProvider.jsx
import React, { useState } from "react";
import { createContext } from "react";
const AuthContext = createContext();
export default AuthContext
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [authed, setAuthed] = useState(false);
function login(user) {
setUser(user);
setAuthed(true);
}
return (
<AuthContext.Provider value={{login, user, authed}}>
{children}
</AuthContext.Provider>
)
}
ProtectedRoute.jsx 到 /home
import React, { useContext, useEffect } from "react";
import AuthContext from "../../context/AuthProvide";
const ProtectedRoute = ({children}) => {
const {login, user, authed} = useContext(AuthContext);
useEffect(() => {
alert("HELLO")
alert(authed)
if (!authed) {
return window.location.href = "/login";
} else {
return children
}
}, [authed, user, login]);
}
export default ProtectedRoute;
Login.jsx
的顶部
import React, { useState, useEffect, useRef, useContext } from "react";
import "./login.css";
import AuthContext from "../../context/AuthProvide";
import { axios } from "../../context/axios";
const LOGIN = "/login";
const Login = () => {
const {login, user, authed} = useContext(AuthContext);
const [userEmail, setUserEmail] = useState("");
const [userPassword, setUserPassword] = useState("");
const [error, setError] = useState("");
const errorRef = useRef();
useEffect(() => {
}, [authed, user, login]);
useEffect(() => {
setError("");
}, [userEmail, userPassword]);
function handleUserEmail(event) {
setUserEmail(event.target.value);
}
function handleUserPassword(event) {
setUserPassword(event.target.value);
}
function handleSubmit(event) {
event.preventDefault();
axios.post(LOGIN, {
email: userEmail,
password: userPassword
}).then(response => {
if (response.data.error) {
setError(response.data.error);
} else {
// this is supposed to be the one to set the user and auth to true
login(response.data.token)
alert(authed)
window.location.href = "/";
}
}).catch(error => {
if (!error?.response) {
setError("NO SERVER RESPONSE");
} else if (error.response?.status === 400) {
setError("MISSING USER NAME OR PASSWORD");
} else if (error.response?.status === 401) {
setError("UNAUTHORIZED ACCESS");
} else {
setError("UNKNOWN ERROR");
}
errorRef.current.focus();
})
}
function resetForm() {
setUserEmail("");
setUserPassword("");
}
return (
// the form is here
)
问题
我发现代码的主要问题是 window.location.href
的使用。使用它时,它会重新加载页面。这将重新安装整个应用程序,任何 React 状态都将是 lost/reset,除非它被持久化到 localStorage 并用于初始化应用程序状态。
更常见的是使用react-router-dom
中的导航工具(我假设这是正在使用的包,但原理翻译)来发布命令式和声明式导航操作。
建议
受保护的路由组件应该重定向到登录路径,或者在用户获得授权的情况下呈现 children
道具。它在路由状态中传递正在访问的当前位置,以便用户可以在成功验证后重定向回来。
import { Navigate } from 'react-router-dom';
const ProtectedRoute = ({ children }) => {
const location = useLocation();
const { authed } = useContext(AuthContext);
if (!authed) {
return <Navigate to="/login" replace state={{ from: location }} />;
} else {
return children;
}
};
Login
组件应该使用 useNavigate
挂钩来使用 navigate
函数在验证后将用户重定向到受保护的路由。
import { useLocation, useNavigate } from 'react-router-dom';
const Login = () => {
const { state } = useLocation();
const navigate = useNavigate();
const { login, user, authed } = useContext(AuthContext);
...
function handleSubmit(event) {
event.preventDefault();
axios.post(
LOGIN,
{
email: userEmail,
password: userPassword
}
)
.then(response => {
if (response.data.error) {
setError(response.data.error);
} else {
login(response.data.token)
navigate(state?.from?.pathname ?? "/", { replace: true });
}
})
.catch(error => {
if (!error?.response) {
setError("NO SERVER RESPONSE");
} else if (error.response?.status === 400) {
setError("MISSING USER NAME OR PASSWORD");
} else if (error.response?.status === 401) {
setError("UNAUTHORIZED ACCESS");
} else {
setError("UNKNOWN ERROR");
}
errorRef.current.focus();
});
}
...
return (
// the form is here
);
}
用 ProtectedRoute
组件包装您要保护的路由。
<AuthProvider>
<Routes>
...
<Route path="/login" element={<Login />} />
<Route
path="/test"
element={
<ProtectedRoute>
<h1>Protected Test Route</h1>
</ProtectedRoute>
}
/>
</Routes>
</AuthProvider>
这里是 React 初学者。我正在使用 jwt、axios 和 useContext 在 React 中构建登录表单。从后端获得授权后,我使用 AuthProvider 将数据存储在全局上下文中并重定向到主页。主页首先检查授权并导航到未经授权的访问登录。但是,即使在登录时更新了 auth (useState),我在第一次点击时仍然会得到一个错误的值,即使经过授权也会被发送回登录。我到处都试过 useEffects 但无济于事。下面的代码
AuthProvider.jsx
import React, { useState } from "react";
import { createContext } from "react";
const AuthContext = createContext();
export default AuthContext
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [authed, setAuthed] = useState(false);
function login(user) {
setUser(user);
setAuthed(true);
}
return (
<AuthContext.Provider value={{login, user, authed}}>
{children}
</AuthContext.Provider>
)
}
ProtectedRoute.jsx 到 /home
import React, { useContext, useEffect } from "react";
import AuthContext from "../../context/AuthProvide";
const ProtectedRoute = ({children}) => {
const {login, user, authed} = useContext(AuthContext);
useEffect(() => {
alert("HELLO")
alert(authed)
if (!authed) {
return window.location.href = "/login";
} else {
return children
}
}, [authed, user, login]);
}
export default ProtectedRoute;
Login.jsx
的顶部import React, { useState, useEffect, useRef, useContext } from "react";
import "./login.css";
import AuthContext from "../../context/AuthProvide";
import { axios } from "../../context/axios";
const LOGIN = "/login";
const Login = () => {
const {login, user, authed} = useContext(AuthContext);
const [userEmail, setUserEmail] = useState("");
const [userPassword, setUserPassword] = useState("");
const [error, setError] = useState("");
const errorRef = useRef();
useEffect(() => {
}, [authed, user, login]);
useEffect(() => {
setError("");
}, [userEmail, userPassword]);
function handleUserEmail(event) {
setUserEmail(event.target.value);
}
function handleUserPassword(event) {
setUserPassword(event.target.value);
}
function handleSubmit(event) {
event.preventDefault();
axios.post(LOGIN, {
email: userEmail,
password: userPassword
}).then(response => {
if (response.data.error) {
setError(response.data.error);
} else {
// this is supposed to be the one to set the user and auth to true
login(response.data.token)
alert(authed)
window.location.href = "/";
}
}).catch(error => {
if (!error?.response) {
setError("NO SERVER RESPONSE");
} else if (error.response?.status === 400) {
setError("MISSING USER NAME OR PASSWORD");
} else if (error.response?.status === 401) {
setError("UNAUTHORIZED ACCESS");
} else {
setError("UNKNOWN ERROR");
}
errorRef.current.focus();
})
}
function resetForm() {
setUserEmail("");
setUserPassword("");
}
return (
// the form is here
)
问题
我发现代码的主要问题是 window.location.href
的使用。使用它时,它会重新加载页面。这将重新安装整个应用程序,任何 React 状态都将是 lost/reset,除非它被持久化到 localStorage 并用于初始化应用程序状态。
更常见的是使用react-router-dom
中的导航工具(我假设这是正在使用的包,但原理翻译)来发布命令式和声明式导航操作。
建议
受保护的路由组件应该重定向到登录路径,或者在用户获得授权的情况下呈现 children
道具。它在路由状态中传递正在访问的当前位置,以便用户可以在成功验证后重定向回来。
import { Navigate } from 'react-router-dom';
const ProtectedRoute = ({ children }) => {
const location = useLocation();
const { authed } = useContext(AuthContext);
if (!authed) {
return <Navigate to="/login" replace state={{ from: location }} />;
} else {
return children;
}
};
Login
组件应该使用 useNavigate
挂钩来使用 navigate
函数在验证后将用户重定向到受保护的路由。
import { useLocation, useNavigate } from 'react-router-dom';
const Login = () => {
const { state } = useLocation();
const navigate = useNavigate();
const { login, user, authed } = useContext(AuthContext);
...
function handleSubmit(event) {
event.preventDefault();
axios.post(
LOGIN,
{
email: userEmail,
password: userPassword
}
)
.then(response => {
if (response.data.error) {
setError(response.data.error);
} else {
login(response.data.token)
navigate(state?.from?.pathname ?? "/", { replace: true });
}
})
.catch(error => {
if (!error?.response) {
setError("NO SERVER RESPONSE");
} else if (error.response?.status === 400) {
setError("MISSING USER NAME OR PASSWORD");
} else if (error.response?.status === 401) {
setError("UNAUTHORIZED ACCESS");
} else {
setError("UNKNOWN ERROR");
}
errorRef.current.focus();
});
}
...
return (
// the form is here
);
}
用 ProtectedRoute
组件包装您要保护的路由。
<AuthProvider>
<Routes>
...
<Route path="/login" element={<Login />} />
<Route
path="/test"
element={
<ProtectedRoute>
<h1>Protected Test Route</h1>
</ProtectedRoute>
}
/>
</Routes>
</AuthProvider>