如何在使用高阶组件进行身份验证时延迟检查redux存储

how to delay checking redux store when authenticating using Higher Order Components

本文关键字:时延 身份验证 延迟 检查 存储 redux 组件 高阶      更新时间:2023-09-26

我使用的是react、redux、react路由器和更高阶的组件。我有一些受保护的路由,用户需要登录才能查看,我已经将这些路由封装在一个更高阶的组件中,我会尽可能清楚地给出上下文,所以请耐心等待。这是我的高阶组件(HOC):

import React from 'react';
import {connect} from 'react-redux';
import { browserHistory } from 'react-router';
export function requireAuthentication(Component, redirect) {
class AuthenticatedComponent extends React.Component {
    componentWillMount() {
        this.checkAuth();
    }
    componentWillReceiveProps(nextProps) {
        this.checkAuth();
    }
    checkAuth() {
        if (!this.props.isAuthenticated) {
            browserHistory.push(redirect);
        }
    }
    render() {
        console.log('requireAuthentication render');
        return (
            <div>
                {this.props.isAuthenticated === true
                    ? <Component {...this.props}/>
                    : null
                }
            </div>
        )
    }
}
const mapStateToProps = (state) => ({
    isAuthenticated: state.loggedIn
});
return connect(mapStateToProps)(AuthenticatedComponent);
}

HOC对redux存储进行检查,以查看用户是否已登录,并且它能够这样做,因为返回的Auth组件已连接到redux存储。

这就是我在使用react路由器的路由中使用HOC的方式:

import React, { Component } from 'react';
import { Router, Route, browserHistory, IndexRoute } from 'react-router';
import Nav from './nav';
import MainBody from './main-body';
import Login from './login';
import Signup from '../containers/signup';
import Dashboard from './dashboard';
import { requireAuthentication } from './require-authentication';
export default class Everything extends Component {
  render() {
    return (
        <div>
          <a className="iconav-brand intelliagg-logo-container" href="/">
            <span className="iconav-brand-icon intelliagg">Deep-Light</span>
          </a>
          <Router history={browserHistory}>
            <Route path='/' component={Login} >
            </Route>
            <Route path='/main' component={MainBody}>
              <Route path='/dashboard' component={ requireAuthentication(Dashboard, '/') } />
              <Route path='/signup' component={Signup} />
            </Route>
          </Router>
        </div>
    );
  }
}

我导入HOC功能,并将受保护的路由传递给HOC,在本例中为Dashboard。如果用户未登录并尝试访问此视图,他们将被重定向到登录页面。

当用户启动登录时,这是由一个操作创建者完成的,该创建者使用用户提供的用户名和密码向服务器发出请求,我有一个reducer,它用一个写着isAuthenticated: true:的标志更新状态

import { browserHistory } from 'react-router';
export default function(state = false, action) {
  let newState;
  switch (action.type) {
    case 'LOGGED_IN':
      newState = action.payload.data.login_status;
      return newState;
      break;
    default:
      return state;
  }
}

我的减速器:

import { combineReducers } from 'redux';
import formFields from './reducer_form-fields';
import hintReducer from './reducer_hint-reducer';
import errorMessage from './reducer_error-message-submission';
import loggedIn from './reducer_logged-in';
import isAuthenticated from './reducer_isAuthenticated';
const rootReducer = combineReducers({
  formInput: formFields,
  hint: hintReducer,
  errorMessage,
  loggedIn,
  isAuthenticated
});
export default rootReducer;

因此,我的问题是HOC组件总是检查isAuthenticated标志,如果它被设置为false(默认值),它会将用户重定向到登录页面。由于登录请求需要一些时间,并且状态需要更新,这意味着用户将始终重定向到登录页面,因为从HOC到redux存储的检查在客户端是即时的。

我如何根据服务器的响应进行检查并相应地重定向用户?有没有办法延迟从HOC检查存储中的冗余值?

我不同意Hayo的观点,认为使用onEnter是最好的方法。请参阅这篇SO文章,了解为什么我认为HOC解决方案(就像你使用的)是一种更好的方法:Universal Auth Redux&React路由器。

至于您的特定问题,您可以通过在reducer中添加一个isAuthenticating布尔值并在checkAuth()函数期间将其传递到HOC中来实现这一点。如果是真的,您可能希望显示一个替代组件,如加载条/微调器等,以向用户提供一些反馈。reducer可以将此bool默认为true,然后在LOGGED_IN上将其设置为false。你的减速器可能看起来像这样:

const initialState = { isAuthenticating: true, isAuthenticated: false };
export default function(state = initialState, action) {
  let newState;
  switch (action.type) {
    case 'LOGGED_IN':
      newState = { isAuthenticating: false, isAuthenticated: action.payload.data.login_status };
      return newState;
    default:
      return state;
  }
}

然后在HOC的render()中,如果isAuthenticating为true,则可以返回LoadingComponent而不是null。

这与我们刚刚在本PR中添加到redux auth包装器的功能非常相似https://github.com/mjrussell/redux-auth-wrapper/pull/35如果您想将其用于更多的参考

我认为应该通过使用react router配置动态路由来解决问题。您的解决方案试图在react组件中而不是在react路由器中解决问题。

换句话说,如果isAutheniated为false,则转到路由"/"。如果是Authenicated,则路由将转到仪表板或请求的任何内容。

查看页面末尾的Configuration with Plain Routes和getComponent函数。React Router也有一个onEnter属性,在您的情况下可能类似于以下属性:

module.exports = store => ({
component: require('./main.jsx'),
childRoutes: [
   // public routes
   { getComponents(nextState, cb) {
       return require.ensure([], (require) => {
                cb(null, require('./login'))
       })
   },{
   ...
   },{
      onEnter: (nextState, replace) => {
        const { isAuthenticated } = store.getState();
        if (!isAuthenticated) {
           replace('/');
        }
     },
     // Protected routes
     childRoutes: [...  ]
   },{
     // Share the path (magic is here)
     // Dynamically load the correct component
     path: '/',
     getComponent: (location, cb) => {
          const { isAuthenticated } = store.getState();
          if (isAuthenticated) 
              return require.ensure([], (require) => {
                       cb(null, require('./dashboard'))
              })
          }
          return require.ensure([], (require) => {
              cb(null, require('./signup'))
          })
      }
    }  

我希望这对解决问题有帮助,并给我们一个思路。