React useContext дает вывод после задержки - PullRequest
2 голосов
/ 17 июня 2020

Мне нужно использовать данные, поступающие из useContext, для аутентификации, если пользователь вошел в систему как Admin или нет. Но он сначала дает значение «undefined», а затем после задержки в 5 секунд дает фактическое значение . Но к тому времени код ломается, потому что он не может работать с "undefined".

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>

};

Нужно ли мне использовать async / await? Потому что я пытался сделать это в пользовательской функции asyn c, и это выдавало другую ошибку, что мы не можем использовать useContext в функции без реакции.

Ответы [ 3 ]

0 голосов
/ 17 июня 2020

Возможно, вы захотите расширить компонент Route и добавить состояние loading в свой AuthContext (и, возможно, ошибку) во время аутентификации.

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>

Обновление: есть очень полезный пример с веб-сайта реактивного маршрутизатора с этим рабочим процессом https://reacttraining.com/react-router/web/example/auth-workflow

0 голосов
/ 17 июня 2020

Я взял идеи из комментариев и нашел обходной путь. Это тоже отлично работает:

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;

Я надеюсь, что это имеет смысл, поскольку это работает для меня. (Сообщите мне, есть ли здесь какие-либо проблемы с безопасностью)

0 голосов
/ 17 июня 2020

Вы можете настроить ловушку useEffect для выполнения побочного эффекта (перенаправление на «/») при изменении состояния (userData):

Заменить:

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. 

Подробнее о Использование обработчика эффектов

Затем вы можете возвращать разные компоненты в зависимости от различных ситуаций:

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