Реагирующие перехваты PrivateRoute на основе вызова API не работают при обновлении страницы

#reactjs #axios #react-hooks

#reactjs #axios #реагирует на перехваты

Вопрос:

У меня есть этот приватный маршрут:

 import React, { useRef, useEffect } from 'react';
import { Route, Redirect } from 'react-router-dom';
import axios from 'axios';

const PrivateRoute = ({ component: Component, ...other }) => {
  const loggedRef = useRef(false);
  const authData = JSON.parse(localStorage.getItem('authData'));

  useEffect(() => {
    const checkAuth = () => {
      axios({
        method: 'post',
        url: `${process.env.REACT_APP_API_URL}auth`,
        headers: { 'content-type': 'application/json' },
        data: {
          email: authData ? authData.email : '',
          client_secret: authData ? authData.client_secret : '',
        },
      })
        .then(function (response) {
          // setLogged(true);
          loggedRef.current = true;
          console.log(loggedRef.current, 'ref');
        })
        .catch(function (error) {
          console.log(error, 'error?');
          loggedRef.current = false;
        });
    };
    checkAuth();
  }, [authData]);

  return (
    <Route
      {...other}
      render={(props) =>
        loggedRef.current === true ? (
          <Component {...props} />
        ) : (
          <Redirect
            to={{
              pathname: '/login',
            }}
          />
        )
      }
    />
  );
};

export default PrivateRoute;
  

Мой Routes.js:

 import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import { CssBaseline } from '@material-ui/core';
import Login from './layout/Login';
import PrivateRoute from './auth/PrivateRoute';
import Homepage from './layout/Homepage';

function App() {
  return (
    <Router>
      <CssBaseline />
      <Route exact path="/login" component={Login} />
      <PrivateRoute exact path="/" component={Homepage} />
    </Router>
  );
}

export default App;
  

authData это элемент localStorage, который создается при успешном входе пользователя в систему. Затем Login компонент пытается перейти на домашнюю страницу с помощью history.push('/') . И '/' вызывается компонентом PrivateRoute, который должен отправить запрос на сервер, чтобы проверить, является ли сохраненный authData действительным.

При первой попытке это работает просто отлично, и я успешно перенаправляю на / но если я обновлю страницу, я вернусь к /login . Что я могу сделать, чтобы сделать его постоянным?

Ранее я использовал useState , но он не работает должным образом с асинхронными функциями, поэтому я использую useRef .

@редактировать

Если необходимо, это мой Login компонент:

 import React, { useState } from 'react';
import axios from 'axios';
import { Redirect, useHistory } from 'react-router-dom';
import {
  StyledContainer,
  StyledButton,
  StyledForm,
  StyledContentWrapper,
  StyledButtonWrapper,
  StyledErrorMessage,
} from './styled';
import EmailIcon from '../../assets/img/email-icon.svg';
import LockIcon from '../../assets/img/lock-icon.svg';
import { TextInput } from '../../components/TextInput';
import { Logo } from '../../components/Logo';
import { Loading } from '../../components/Loading';
import { Title } from '../../components/Title';

const Login = () => {
  const [error, setError] = useState(false);
  const [loading, setLoading] = useState(false);
  const history = useHistory();

  const onSubmitHandler = (event) => {
    event.preventDefault();
    const { email, client_secret } = event.target.elements;
    setLoading(true);
    axios({
      method: 'post',
      url: `${process.env.REACT_APP_API_URL}auth`,
      headers: { 'content-type': 'application/json' },
      data: { email: email.value, client_secret: client_secret.value },
    })
      .then(function (response) {
        const authData = {
          id_token: response.data.id_token,
          email: email.value,
          client_secret: client_secret.value,
        };
        console.log(response);
        localStorage.setItem('authData', JSON.stringify(authData));
        // window.location.href = '/'; // with this, it works.
        history.push('/'); //if I use history.push, the redirect will not work.
      })
      .catch(function (error) {
        console.log(error);
        setLoading(false);
        setError(true);
      });
  };
  // if (localStorage.getItem('authData')) {
  //   return <Redirect to="/" />;
  // }
  const inputFields = [
    {
      autoComplete: 'email',
      autoFocus: true,
      name: 'email',
      required: true,
      fullWidth: true,
      margin: 'normal',
      id: 'email',
      placeholder: 'E-mail',
      type: 'email',
      icon: <img src={EmailIcon} className="input-icon" alt="E-mail icon" />,
      error: error,
    },
    {
      autoComplete: 'current-password',
      name: 'client_secret',
      required: true,
      fullWidth: true,
      margin: 'normal',
      placeholder: 'Token de acesso',
      id: 'client_secret',
      icon: <img src={LockIcon} className="input-icon" alt="Lock icon" />,
      error: error,
      isPassword: true,
    },
  ];

  return (
    <StyledContainer component="main" maxWidth="xs">
      {loading amp;amp; <Loading />}
      <StyledContentWrapper>
        <Logo />
        <Title size="1.5rem" margin="4.188rem 0 0 0">
          BEM-VINDO
        </Title>
        <Title size="1.125rem" weight="normal" margin="1.281rem 0 0 0">
          Lorem ipsum dolor sit amet, contetur
          <br /> adipiscing elit. Nunc accumsan.
        </Title>
        <StyledForm onSubmit={onSubmitHandler}>
          {inputFields.map((field, index) => (
            <TextInput key={`${field.name}_${index}`} {...field} />
          ))}
          {error amp;amp; (
            <StyledErrorMessage>
              Credenciais informadas são inválidas, tente novamente
            </StyledErrorMessage>
          )}
          <StyledButtonWrapper>
            <StyledButton
              type="submit"
              variant="contained"
              color="primary"
              error={error.toString()}
            >
              Entrar
            </StyledButton>
          </StyledButtonWrapper>
        </StyledForm>
      </StyledContentWrapper>
    </StyledContainer>
  );
};

export default Login;
  

Ответ №1:

Вместо использования Redirect компонента вы пробовали использовать вызов history.push('/login') при неудачной аутентификации (т. Е. В вашем предложении catch)?

Возможно, вам потребуется сначала получить доступ к объекту истории с помощью useHistory prop:

 import {useHistory} from 'react-router-dom'

// Within functional component
const history = useHistory()
  

Вы также можете отложить загрузку компонента, добавив логическое значение состояния загрузки, которое вы начинаете как false, а затем переключаетесь на true после завершения асинхронного вызова и условно отображаете свой компонент после завершения загрузки.

Оцените, что это архитектура, отличная от той, которую вы используете в настоящее время, но я думаю, что переключение на этот способ может одним из способов решить вашу проблему

Комментарии:

1. Я думаю, что основная проблема заключается в том, что React не ожидает, чтобы увидеть, loggedRef верно ли, и отобразить компонент. history.push похоже, это не помогает 🙁

2. Я только что добавил свой компонент входа, idk, если это актуально..

3. Хорошо, в таком случае, возможно, попробуйте реализовать часть состояния загрузки, чтобы компонент отображался только тогда, когда загруженное состояние истинно (т. Е. асинхронность завершена). Вы могли бы отобразить экран типа «загрузочный счетчик», когда загружено значение false, а затем загрузить свой основной компонент, когда загружено значение true