React上下文状态在尝试使用来自Cookie的令牌时未更新。

huangapple go评论103阅读模式
英文:

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) =&gt; {
    const [user, setUser] = useState&lt;AuthUser | null&gt;(null);
    const [isAuthenticated, setIsAuthenticated] = useState(false);
    const [isValidated, setIsValidated] = useState(false);

    var cookies = document.cookie.split(&quot;;&quot;);
    useEffect(() =&gt; {        
        var auth = cookies.filter(cookie =&gt; {            
            return cookie.split(&quot;=&quot;)[0].includes(&quot;authorization&quot;)
        })
        
        if (auth.length !== 0) {
            setIsAuthenticated(true);      
            var userInfo = jwt.decode(auth[0].split(&quot;=&quot;)[1])      
            setUser(userInfo)            
        }
    }, [])

    const values = {
        user,
        setUser,
        isAuthenticated,
        setIsAuthenticated,
        isValidated,
        setIsValidated,
    }
 
    return (
        &lt;AuthContext.Provider value={values}&gt;{children}&lt;/AuthContext.Provider&gt;
    )
}

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(() =&gt; {
    const cookies = document.cookie-split(&quot;;&quot;);
    const token = cookies[cookies. length - 1].split(&quot;,&quot;);

    console.log (token);

    if (token[0].split(&quot;=&quot;).includes(&quot;authorization&quot;)) {
       setIsAuthenticated(true);
    }
  }, [])

return(
  &lt;Routes&gt;
    &lt;Route path=&quot;/&quot; element={&lt;Home /&gt;} /&gt;
    &lt;Route path=&quot;/request-access&quot; element={&lt;RequestAccess /&gt;} /&gt;
    // protected routes
    &lt;Route path=&quot;/verify-email&quot; element={
      &lt;ProtectedRoute isRouteAccessible={isAuthenticated}&gt;
        &lt;VerifyEmail/&gt;
      &lt;/ProtectedRoute&gt;
    }
    /&gt;
  &lt;/Routes&gt;
)

It might be worth mentioning that for some reason, my state does update immediately when set like this:

await axios.post(url + &quot;login&quot;, payload)
        .then(res =&gt; {
            var token = jwt.decode(res.headers.authorization.split(&quot; &quot;)[1]);                               
            context.setUser(token.User);
            context.setIsAuthenticated(true);
            console.log(context)
            document.cookie = `authorization=${res.headers.authorization.split(&quot; &quot;)[1]}`            
            resolve(&#39;Success&#39;);
        })
        .catch(err =&gt; {     
            console.log(err);           
            reject(&quot;bad request&quot;)
        })

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 = () =&gt; {
  const cookies = document.cookie - split(&quot;;&quot;);
  const token = cookies[cookies.length - 1].split(&quot;,&quot;);

  console.log(token);

  if (token[0].split(&quot;=&quot;).includes(&quot;authorization&quot;)) {
    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

huangapple
  • 本文由 发表于 2023年6月13日 18:26:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76463939.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定