【问题标题】:React useContext is gives output after delayReact useContext 在延迟后给出输出
【发布时间】:2020-06-17 16:21:40
【问题描述】:

我需要使用来自 useContext 的数据来验证登录的用户是否是管理员。但它首先给出值“未定义”,然后在延迟 5 秒后给出实际值。 但是到那时,代码会中断,因为它不能与“未定义”一起使用。

import React, { useContext, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { AuthContext } from '../../context/AuthContext';

const Admin = () => {
    const history = useHistory();
    const { userData } = useContext(AuthContext);
    console.log(userData); // ****** This first gives {token: undefined, user: undefined}
    /***************************** Then it gives the actual data after 5-10 seconds delay
     * {token: "eyJhbGciOiJIUzI", user: {…}}
        token: "eyJhbGciOiJIUzI"
        user:
        isAdmin: true
        __v: 0
        _id: "5eea03a736b70722c83a7b63"
    *******************************************************************/
    // I WANT TO FETCH "isAdmin" // so that I can work on further code
    if (!userData.user.isAdmin) {
        history.push("/");
    };
    return <h1>Admin Panel</h1>

};

我需要使用异步/等待吗?因为我尝试在自定义异步函数中执行此操作,但它引发了另一个错误,我们无法在非反应函数中使用 useContext。

【问题讨论】:

  • 您在哪里进行抓取?我的意思是,5 秒后它来自哪里?
  • console.log(userData) 每次页面呈现时显示两次
  • 也许您可以在页面尝试获取 auth 用户数据时说“正在加载”。 function getPage(userData){ if (!userData.user) { return

    loading... .... } 并使用它作为回报,例如return {getPage(userData)}

标签: javascript reactjs react-redux react-router


【解决方案1】:

我从 cmets 那里获得了一些想法并找到了解决方法。 这也很好用:

import React, { useContext, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { AuthContext } from '../../context/AuthContext';
import AdminPanel from './AdminPanel';

const Admin = () => {
    const token = localStorage.getItem('auth-token');
    const history = useHistory();
    const { userData } = useContext(AuthContext);

    //send item to backend
    const saveNewItem = async (data) => {
        const url = "http://localhost:5000/api/items";
        try {
            const res = await axios.post(url, data, {
                headers: { "x-auth-token": token }
            });
            console.log(res.data);    
        } catch (error) {
            console.log(error.response);
        }
    };

    /********************************
     * Handling Authentication logic
     ********************************/
    };
    if (!token) {
        history.push("/");
    };
    /********************************
     * Handling Authentication logic
     ********************************/

    return <div>
        {
            userData.user !== undefined && userData.user.isAdmin 
            ? <AdminPanel saveNewItem={saveNewItem}/>
            :
            <div className="container p-5 text-center">
            <div class="spinner-border text-light" role="status">
                <span class="sr-only">Loading...</span>
            </div>
            </div>   
        }
        {/* <AdminPanel saveNewItem={saveNewItem}/> */}
    </div>

};
export default Admin;

我希望这对我有用。 (如果这里的安全性有任何问题,请告诉我)

【讨论】:

    【解决方案2】:

    您可以设置一个useEffect 钩子以在状态(用户数据)更改时执行副作用(重定向到“/”):

    替换:

    if (!userData.user.isAdmin) {
      history.push("/");
    }
    

    与:

    useEffect(() => {
      if (userData && userData.user && !userData.user.isAdmin) {
        history.push("/");
      }
    }, [userData]); // tell React to skip applying this effect if userData hasn't changed between re-renders. 
    

    更多关于Using the Effect Hook

    那么,你可以根据不同的情况返回不同的组件:

    if (!userData) return <p>Loading</p>;
    if (!userData.user.isAdmin) return <p>You are not an admin. Redirecting...</p>;
    return <h1>Admin Panel</h1>;
    

    【讨论】:

    • 但这并不能解决限制其他人手动路由到 /admin 的问题。在这种情况下,他们可以手动进入管理页面。我还能做些什么吗?
    • 如果没有用户数据(获取时)或用户不是管理员,则应返回一些加载组件或 null:if (!userData || (userData.user &amp;&amp; !userData.user.isAdmin)) return null;
    【解决方案3】:

    您可能需要考虑扩展 Route 组件并在验证时在 AuthContext 上添加 loading 状态(可能是错误)。

    AdminOnlyRoute.js

    import React from "react";
    import { Redirect, useLocation, Route } from "react-router-dom";
    import { AuthContext } from '../../context/AuthContext';
    
    const AdminOnlyRoute = (props) => {
      // give the context a loading and or an error state
      const { userData, loading, error } = useContext(AuthContext);
    
      if (loading) {
        // Some loader maybe?
        return <div>Authenticating...</div>
      }
    
      if (error) {
        return <div>Authentication error.</div>
      }
    
      if (userData && !userData.user.isAdmin)
        return (
          <Redirect to="/" />
        );
    
      return <Route {...props} />;
    };
    
    export default AdminOnlyRoute;
    
    

    你可以这样使用这个组件:

    <Router>
      <Switch>
        <AdminOnlyRoute path="/admin" component={<div>Admin</div>} />
        <Route path="/" component={<div>Home</div>} />
      </Switch>
    </Router>
    

    更新:react-router 网站上有一个非常有用的示例,其中包含此工作流程 https://reacttraining.com/react-router/web/example/auth-workflow

    【讨论】:

    • 谢谢。我根据您的建议发布了答案。你能说出这在生产中是否可以很好地工作吗?
    猜你喜欢
    • 2021-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-27
    相关资源
    最近更新 更多