英文:
React Context State Is Not Updating When Trying to Use Token From Cookies
问题
以下是您要翻译的内容:
在 React Web 应用中,保持上下文状态的最常见方式是将用户信息存储在本地存储或 Cookie 中的令牌等地方,然后在每次页面加载时检索令牌并重新设置上下文状态,因为状态在页面加载之间不会持续存在。
这是绝大多数文章和 Stack Overflow 回答教导我的方法,我已经尝试在这里实现它:
export const AuthProvider = ({children}: AuthContextProviderType) => {
const [user, setUser] = useState<AuthUser | null>(null);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [isValidated, setIsValidated] = useState(false);
var cookies = document.cookie.split(";");
useEffect(() => {
var auth = cookies.filter(cookie => {
return cookie.split("=")[0].includes("authorization")
})
if (auth.length !== 0) {
setIsAuthenticated(true);
var userInfo = jwt.decode(auth[0].split("=")[1])
setUser(userInfo)
}
}, [])
const values = {
user,
setUser,
isAuthenticated,
setIsAuthenticated,
isValidated,
setIsValidated,
}
return (
<AuthContext.Provider value={values}>{children}</AuthContext.Provider>
)
}
问题是,当我需要更新状态时,我的状态没有更新。在我登录并进入我的用户仪表板后,这是受保护的路由,我刷新页面,就被踢出到登录页面,这意味着我的上下文,用于确定是否可以访问受保护的路由,没有被更新。用户状态当然也没有更新。
我还尝试跳过上下文步骤,直接使用令牌来确定用户是否可以访问受保护的路由。然而,这也没有奏效:
const [isAuthenticated, setIsAuthenticated] = useState(false);
useEffect(() => {
const cookies = document.cookie.split(";");
const token = cookies[cookies.length - 1].split(",");
console.log(token);
if (token[0].split("=").includes("authorization")) {
setIsAuthenticated(true);
}
}, [])
return(
<Routes>
<Route path="/" element={<Home />} />
<Route path="/request-access" element={<RequestAccess />} />
// 受保护的路由
<Route path="/verify-email" element={
<ProtectedRoute isRouteAccessible={isAuthenticated}>
<VerifyEmail/>
</ProtectedRoute>
}
/>
</Routes>
)
值得一提的是,出于某种原因,只要像这样设置状态,我的状态会立即更新:
await axios.post(url + "login", payload)
.then(res => {
var token = jwt.decode(res.headers.authorization.split(" ")[1]);
context.setUser(token.User);
context.setIsAuthenticated(true);
console.log(context)
document.cookie = `authorization=${res.headers.authorization.split(" ")[1]}`
resolve('Success');
})
.catch(err => {
console.log(err);
reject("bad request")
})
总结一下,我只需一种方法来更新和持久保存用户数据,以及一个表示用户已验证的布尔值,以便用户可以“登录”。
提前感谢您。
英文:
It is my understanding that the most common way of persisting context state in a React web app is to store the user’s information in something like a token in local storage or a cookie, and then, on each page load, retrieving the token and setting the context state again, since state doesn’t persist between page loads.
That is what the vast majority of articles and SO answers have instructed me to do and I’ve tried to implement it here:
export const AuthProvider = ({children}: AuthContextProviderType) => {
const [user, setUser] = useState<AuthUser | null>(null);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [isValidated, setIsValidated] = useState(false);
var cookies = document.cookie.split(";");
useEffect(() => {
var auth = cookies.filter(cookie => {
return cookie.split("=")[0].includes("authorization")
})
if (auth.length !== 0) {
setIsAuthenticated(true);
var userInfo = jwt.decode(auth[0].split("=")[1])
setUser(userInfo)
}
}, [])
const values = {
user,
setUser,
isAuthenticated,
setIsAuthenticated,
isValidated,
setIsValidated,
}
return (
<AuthContext.Provider value={values}>{children}</AuthContext.Provider>
)
}
The problem is that my state is not updating when I need it to. After I log myself in and I am in my user dashboard, which is a protected route, I refresh the page and I’m kicked out to the login screen, meaning that my context that determines whether I can go to protected routes is not being updated. The user state is not updated either, of course.
I have also tried skipping the context step and going straight to the token to determine if a user can visit a protected route. However, this has not worked either:
const [isAuthenticated, setIsAuthenticated] = useState(false);
useEffect(() => {
const cookies = document.cookie-split(";");
const token = cookies[cookies. length - 1].split(",");
console.log (token);
if (token[0].split("=").includes("authorization")) {
setIsAuthenticated(true);
}
}, [])
return(
<Routes>
<Route path="/" element={<Home />} />
<Route path="/request-access" element={<RequestAccess />} />
// protected routes
<Route path="/verify-email" element={
<ProtectedRoute isRouteAccessible={isAuthenticated}>
<VerifyEmail/>
</ProtectedRoute>
}
/>
</Routes>
)
It might be worth mentioning that for some reason, my state does update immediately when set like this:
await axios.post(url + "login", payload)
.then(res => {
var token = jwt.decode(res.headers.authorization.split(" ")[1]);
context.setUser(token.User);
context.setIsAuthenticated(true);
console.log(context)
document.cookie = `authorization=${res.headers.authorization.split(" ")[1]}`
resolve('Success');
})
.catch(err => {
console.log(err);
reject("bad request")
})
To summarize, I simply need a way to update and persist a user’s data a boolean that states the user has been verified, so a user can be “logged in”.
Thank you in advance.
答案1
得分: 1
When you're refreshing, isAuthenticated
initially is set to false. So, initial render is false. At that time, isRouteAccessible
will return false.
useEffect
will run after that render, so isAuthenticated
then becomes true if cookies are there. But by this time, the url is/has already been changed to some public path.
So, you need to set isAuthenticated
, with true or false, at the initial render only. You can pass a callback to useState for getting the initial value.
I created a SandBox using localStorage to implement the same. Please go through that, if you have doubts.
Try something like this.
const initialState = () => {
const cookies = document.cookie.split(";");
const token = cookies[cookies.length - 1].split(",");
console.log(token);
if (token[0].split("=").includes("authorization")) {
return true;
}
return false;
};
const [isAuthenticated, setIsAuthenticated] = useState(initialState);
Refer to this answer as well, it has been explained there as well: https://stackoverflow.com/a/70764325/6065749
英文:
When you're refreshing, isAuthenticated
initially is set to false. So, initial render is false. At that time, isRouteAccessible
will return false.
useEffect
will run after that render, so isAuthenticated
then becomes true if cookies are there. But by this time, the url is/has already been changed to some public path.
So, you need to set isAuthenticated
, with true or false, at the initial render only. You can pass a callback to useState for getting the initial value.
I created a SandBox using localStorage to implement the same. Please go through that, if you have doubts.
Try something like this.
const initialState = () => {
const cookies = document.cookie - split(";");
const token = cookies[cookies.length - 1].split(",");
console.log(token);
if (token[0].split("=").includes("authorization")) {
return true;
}
return false;
};
const [isAuthenticated, setIsAuthenticated] = useState(initialState);
Refer to this answer as well, it has been explained there as well : https://stackoverflow.com/a/70764325/6065749
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论