reactjs - React Router - Cuando la actualización no autenticada en páginas de ruta simples me redirige a la página de índice

CorePress2024-01-24  8

Cuando un usuario está autenticado, puedo actualizar la página y acceder a todas las rutas a través del sitio web o ingresando/actualizando la URL. Sin embargo, cuando el usuario no está autenticado, aunque la ruta a través del sitio web funciona bien, actualizar la URL incluso en páginas de ruta simple (no privadas) me redirige al índice (página principal).

He usado esto como plantilla de mi aplicación de reacción y esto para agregar autenticación a mi aplicación de reacción. Probé estas dos guías por separado y ambas funcionan bien, pero de alguna manera en mi aplicación de reacción su combinación genera el problema que describí anteriormente.

A continuación se muestra el código para las rutas de mi aplicación:

import React, { Component, lazy, Suspense} from 'react';
import { Switch, Route, Redirect } from 'react-router-dom';

import Spinner from '../app/shared/Spinner';


const MainIndex = lazy(() => import( "./mainpage/Index"));
const TermsIndex = lazy(() => import( "./mainpage/Terms"));
const Dashboardmain = lazy(() => import( "./dashboard/Dashboardmain"));
const Login = lazy(() => import( "./user-pages/Login"));
const Register = lazy(() => import( "./user-pages/Register"));


class AppRoutes extends Component {
  render () {
    return (

        <Suspense fallback={<Spinner/>}>
          <Switch>
            <Route exact path="/login" component={ Login } />
            <Route exact path="/index" component={ MainIndex } />
            <Route exact path="/terms" component={ TermsIndex } />

            <PrivateRoute exact path="/internal/dashboard" component={ Dashboardmain } />

            <Route exact path="/register" component={ Register } />

          </Switch>
        </Suspense>

    );
  }
}

export default AppRoutes;

Las páginas de mi aplicación se ven así: FullPageLayout sirve para comprobar si se debe agregar encabezado, pie de página o barra lateral a la página o no:

import React, { Component } from 'react';
import AppRoutes from './AppRoutes';
import Navbar from './shared/Navbar';
import Sidebar from './shared/Sidebar';
import Footer from './shared/Footer';
import { withRouter } from 'react-router-dom';

class AppPages extends Component {
  state = {}
  componentDidMount() {
    this.onRouteChanged();
  }

  render () {

    let navbarComponent = !this.state.isFullPageLayout ? <Navbar/> : '';
    let sidebarComponent = !this.state.isFullPageLayout ? <Sidebar/> : '';
    let footerComponent = !this.state.isFullPageLayout ? <Footer/> : '';

    return (
          <div className="container-scroller">
            { sidebarComponent }
            <div className="container-fluid page-body-wrapper">
              { navbarComponent }
              <div className="main-panel">
                <div className="content-wrapper">
                  <AppRoutes/>
                </div>
                { footerComponent }
              </div>
            </div>
          </div>
    );
  }

  componentDidUpdate(prevProps) {
    if (this.props.location.pathname !== prevProps.location.pathname) {
      this.onRouteChanged();
    }
  }

  onRouteChanged() {
    const containment = [];
    const fullPageLayoutRoutes = ['/login', '/register', 
    '/index', '/terms'];

    const body = document.querySelector('body');
    body.classList.remove('rtl')
    for (var i = 0; i < fullPageLayoutRoutes.length; i++) {
      containment[containment.length] = this.props.location.pathname.toLowerCase().includes(fullPageLayoutRoutes[i])
    }
    if (containment.includes(true)) {
        this.setState({isFullPageLayout: true})
        document.querySelector('.page-body-wrapper').classList.add('full-page-wrapper');
    } else {
        this.setState({isFullPageLayout: false})
        document.querySelector('.page-body-wrapper').classList.remove('full-page-wrapper');
    }
  }
}

export default withRouter(AppPages);

Und mi aplicación es la siguiente:

import React, { Component } from 'react';
import { BrowserRouter } from 'react-router-dom';
import './App.scss';
import { Provider } from "react-redux";
import jwt_decode from "jwt-decode";
import setAuthToken from "../utils/setAuthToken";
import { setCurrentUser, logoutUser } from "../actions/authActions";
import store from "../store";
import AppPages from './AppPages';


// Check for token to keep user logged in
if (localStorage.jwtToken) {
  // Set auth token header auth
  const token = localStorage.jwtToken;
  setAuthToken(token);
  // Decode token and get user info and exp
  const decoded = jwt_decode(token);
  // Set user and isAuthenticated
  store.dispatch(setCurrentUser(decoded));
  // Check for expired token
  const currentTime = Date.now() / 1000; // to get in milliseconds
  if (decoded.exp < currentTime) {
    // Logout user
    store.dispatch(logoutUser());
    // Redirect to login
    window.location.href = "/login";
  }
}

class App extends Component {
  render () {
    return (
      <Provider store={store}>
        <BrowserRouter>
          <AppPages/>
        </BrowserRouter>
      </Provider>
    );
  }
}

export default App;

Intenté depurar el problema cambiando/simplificando cosas en el enrutador o en la parte de la aplicación, pero no creo que el problema esté aquí. Creo que el problema está en algún lugar de los reductores o acciones.

Mi tienda se define de la siguiente manera:

import { createStore, applyMiddleware, compose } from "redux";
import thunk from "redux-thunk";
import rootReducer from "./reducers";

const initialState = {};
const middleware = [thunk];

const store = createStore(
  rootReducer,
  initialState,
  compose(
    applyMiddleware(...middleware),
    (window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ &&
      window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__()) ||
      compose
  )
);

export default store;

Mi authReducer se define como:

import { SET_CURRENT_USER, USER_LOADING } from "../actions/types";

const isEmpty = require("is-empty");

const initialState = {
  isAuthenticated: false,
  user: {},
  loading: false
};

export default function(state = initialState, action) {
  switch (action.type) {
    case SET_CURRENT_USER:
      return {
        ...state,
        isAuthenticated: !isEmpty(action.payload),
        user: action.payload
      };
    case USER_LOADING:
      return {
        ...state,
        loading: true
      };
    default:
      return state;
  }
}

Mi acción de autenticación se define como:

import axios from "axios";
import setAuthToken from "../utils/setAuthToken";
import jwt_decode from "jwt-decode";

import { GET_ERRORS, SET_CURRENT_USER, USER_LOADING } from "./types";

// Register User
export const registerUser = (userData, history) => dispatch => {
  axios
    .post("/api/users/register", userData)
    .then(res => history.push("/login")) // re-direct to login on successful register
    .catch(err =>
      dispatch({
        type: GET_ERRORS,
        payload: err.response.data
      })
    );
};
// Login - get user token
export const loginUser = userData => dispatch => {
  axios
    .post("/api/users/login", userData)
    .then(res => {
      // Save to localStorage
      // Set token to localStorage
      const { token } = res.data;
      localStorage.setItem("jwtToken", token);
      // Set token to Auth header
      setAuthToken(token);
      // Decode token to get user data
      const decoded = jwt_decode(token);
      // Set current user
      dispatch(setCurrentUser(decoded));
    })
    .catch(err =>
      dispatch({
        type: GET_ERRORS,
        payload: err.response.data
      })
    );
};
// Set logged in user
export const setCurrentUser = decoded => {
  return {
    type: SET_CURRENT_USER,
    payload: decoded
  };
};
// User loading
export const setUserLoading = () => {
  return {
    type: USER_LOADING
  };
};
// Log user out
export const logoutUser = () => dispatch => {
  // Remove token from local storage
  localStorage.removeItem("jwtToken");
  // Remove auth header for future requests
  setAuthToken(false);
  // Set current user to empty object {} which will set isAuthenticated to false
  dispatch(setCurrentUser({}));
};



------------------------------------

Tiene algo que ver con el orden de ejecución. El estado redux se borra cuando actualiza, por lo que necesita que se llame a store.dispatch(setCurrentUser(decoded)) antes de que PrivateRoute intente procesarse. No es obvio para mí exactamente dóndeVa mal porque el bloque if (localStorage.jwtToken) { no es asíncrono, aunque el envío podría serlo.

Recomendaría establecer el estado inicial en authReducer en isAuthenticated: null y actualizarlo a verdadero o falso una vez que haya examinado el token. En este momento su PrivateRoute sólo conoce dos estados: autenticado y no autenticado. Lo necesitamos para entender un tercero que es "todavía no lo sé". En su PrivateRoute no representaría nada o mostraría un control giratorio de carga mientras isAutheticated sea nulo. No muestres el redireccionamiento hasta que tengas un resultado falso definitivo.

5

Establecí el estado inicial en isAuthenticated: null y me di cuenta de que en mi tienda inicialmente es nulo, pero cuando inicio sesión como usuario cambia a verdadero. Sin embargo creo que no me expliqué bien. Mi problema es actualizar páginas no privadas (es decir, página de términos). Cuando inicio sesión, puedo actualizar la URL en la página de términos y se carga correctamente. Pero, si no he iniciado sesión, la actualización de la URL no funciona y me lleva de regreso a la página de inicio de sesión.

- Kamran Esmaeili

26/03/2021 a las 22:13

Eso es rRealmente raro. Términos es una ruta simple, por lo que ni siquiera sé dónde se activaría la redirección.

Linda Paiste

27 de marzo de 2021 a las 0:55

Exactamente, he intentado encontrar dónde o cuándo se activa, pero hasta ahora no he podido localizarlo. En mi intento de depuración, deshabilité PrivateRoutes, if (localStorage.jwtToken) {...}, FullPageLayout y también trabajé con inicialState, pero hasta ahora no tuve éxito. Quizás primero deba encontrar dónde se activa la redirección al índice. ¿Alguna sugerencia en términos de depuración?

- Kamran Esmaeili

27/03/2021 a las 10:04

Sospecho que tiene que ver con la configuración de su servidor más que con su código. Probablemente redirija todas las rutas desconocidas a la raíz.

Linda Paiste

27/03/2021 a las 17:11

1

Gracias Linda, logré descubrir de dónde venía el problema. Mira mi nuevo comentario. Básicamente para mis rutas sencillas aunque no las estaba usando.g el componente navbar, todavía se estaba cargando y verificando para definir el usuario y, si no, me devolvía a la página de índice.

- Kamran Esmaeili

27/03/2021 a las 18:55



------------------------------------

Entonces, después de un poco de depuración, me di cuenta de que mi problema proviene de AppPages y la línea con { navbarComponent }. Esta barra de navegación se utiliza con PrivateRoute y requiere que se defina el usuario. Cuando el usuario no está autenticado, aunque técnicamente esto no se representará para rutas simples, aún se procesa y dentro de esta barra de navegación hay una condición que si el usuario no está definido, regresará a la página de índice.

Su guía para un futuro mejor - libreflare
Su guía para un futuro mejor - libreflare