React Router - 在普通路由页面上未经身份验证的刷新将我重定向到索引页面

时间:2021-03-26 19:18:20

标签: reactjs redux react-router-dom

当用户通过身份验证后,我可以刷新页面并通过网站或输入/刷新 URL 访问所有路由。然而,当用户未经身份验证时,虽然通过网站的路由运行良好,但即使在普通路由页面(非私有)上刷新 URL 也会将我重定向到索引(主页)。

我已使用 this 作为我的 React 应用程序的模板,并使用 this 为我的 React 应用程序添加身份验证。我分别尝试了这两个指南,并且都运行良好,但不知何故在我的 React 应用程序中,它们的组合导致了我上面描述的问题。

以下是我的应用路由代码:

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;

我的应用页面如下所示 - FullPageLayout 用于检查是否应为页面添加页眉、页脚或侧边栏:

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);

我的应用程序如下:

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;

我尝试通过更改/简化路由器或应用程序部分中的内容来调试问题,但我认为问题不在这里。我认为问题出在减速器或操作中。

我的店铺定义如下:

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;

我的 authReducer 定义为:

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;
  }
}

我的 authAction 定义为:

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({}));
};

2 个答案:

答案 0 :(得分:1)

这与执行顺序有关。刷新时 redux 状态会被擦除,因此您需要在 store.dispatch(setCurrentUser(decoded)) 尝试呈现之前调用 PrivateRoute。我不清楚哪里出错了,因为 if (localStorage.jwtToken) { 块不是 async,尽管 dispatch 可能是?

我建议将 initialState 中的 authReducer 设置为 isAuthenticated: null,并在您检查令牌后更新为 truefalse。现在您的 PrivateRoute 只知道两种状态:已验证和未验证。我们需要它来理解第三个“我还不知道”。在您的 PrivateRoute 中,当 isAutheticatednull 时,您将不渲染任何内容或加载微调器。在确定 Redirect 之前不要渲染 false

答案 1 :(得分:1)

所以经过一些调试后,我意识到我的问题来自 AppPages 和带有 { navbarComponent } 的那一行。此导航栏与 PrivateRoute 一起使用,需要定义用户。当用户未经身份验证时,虽然这在技术上不会为普通路由呈现,但它仍在处理,并且在此 navbar 中是一个条件,如果用户未定义,将返回到 index 页面。< /p>

相关问题