使用 react-router 进行模态对话框身份验证

Modal dialog auth with react-router

我的 react/redux/react-router 应用程序包含 publicprivate 部分。 登录和注册表格显示为模态对话框,没有自己的路线。

所需流量: 用户点击 link -> 模式对话框显示在当前页面 -> 如果成功转换到 linked 页面,否则将用户留在当前页面。

如果没有当前页-显示索引页并继续流程

我尝试使用 onEnter 挂钩对此进行存档,但据我所知,转换发生在执行挂钩之前。如果我尝试使用 history.goBack(),它会导致页面重新呈现并且看起来很糟糕。

有什么方法可以解决这个问题而无需不必要的重定向和额外的渲染调用?

好的 - 我想我想出了一种解决这个问题的方法,涵盖了所有极端情况。不过,它确实要求您有某种方法可以从几乎所有组件访问应用程序状态。为此,我正在使用 Redux。这也假设登录、注册等没有路由。

我所做的是创建两个 'wrapper' 组件。第一个包装任何不安全的路由并将位置存储到状态值,以便我们始终引用最后一个不安全的路由...

import { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { setRoute } from './redux/client/ducks/auth';

function mapDispatchToProps(dispatch) {
  return {
    setRoute: bindActionCreators(setRoute, dispatch)
  };
}

@connect(null, mapDispatchToProps)
export default class InsecureWrapper extends Component {

  componentDidMount() {
    const { location, setRoute } = this.props;
    setRoute(location.pathname);
  }

  render() {
    return (
      <div>
        {this.props.children}
      </div>
    )
  }
}

另一个包装所有安全路由。它显示登录对话框(也可以在登录和注册之间来回切换或其他)并防止显示内容,除非登录到应用程序...

import { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as authActions from './redux/client/ducks/auth';

function mapStateToProps(state) {
  return {
    auth: state.auth
  };
}

function mapDispatchToProps(dispatch) {
  return {
    authActions: bindActionCreators(authActions, dispatch)
  };
}

@connect(
  mapStateToProps,
  mapDispatchToProps
)
export default class SecureWrapper extends Component {

  componentDidMount() {
    const { auth, authActions } = this.props;
    //see if user and if not prompt for login
    if(!auth.loggedIn) {
      authActions.openLogin();
    }
  }

  //close any open dialogs in case the user hit browser back or whatever 
  componentWillUnmount() {
    const { authActions } = this.props;
    authActions.resetAuthDialogs();
  }    

  render() {
    const { auth, children } = this.props;
    return (
        <div className="container">
          {auth.loggedIn &&
            {children} ||
            <span>YOU MUST BE LOGGED IN TO VIEW THIS AREA!</span>
          }
        </div>
    );
  }

}

然后在路由中,根据需要将它们包裹在包装器中...

import App from './containers/App';
import Dashboard from './containers/Dashboard';
import Secure from './containers/Secure';
import AuthWrapper from './common/client/components/AuthWrapper';
import InsecureWrapper from './common/client/components/InsecureWrapper';

export default [
  {path: '/', component: App, //common header or whatever can go here
  childRoutes: [
    {component: InsecureWrapper,
      childRoutes: [ //<- ***INSECURE ROUTES ARE CHILDREN HERE***
        {path: 'dashboard', component: Dashboard}
      ]
    },
    {component: SecureWrapper,
      //***SECURE ROUTES ARE CHILDREN HERE***
      childRoutes: [
        {path: 'secure', component:Secure}
      ]
    }
  ]}
]

最后但同样重要的是...在您的对话框中,您需要通过将位置推送(或替换)到保存的状态值来处理取消。当然,在成功登录后,只需关闭它们...

import { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as authActions from './redux/client/ducks/auth';
import LoginDialog from './common/client/components/dialogs/LoginDialog';
import RegisterDialog from './common/client/components/dialogs/RegisterDialog';
// this could also be replacePath if you wanted to overwrite the history
import { pushPath } from 'redux-simple-router'; 

function mapStateToProps(state) {
  return {
    auth: state.auth
  };
}

function mapDispatchToProps(dispatch) {
  return {
    authActions: bindActionCreators(authActions, dispatch),
    pushPath: bindActionCreators(pushPath, dispatch),
  };
}

@connect(
  mapStateToProps,
  mapDispatchToProps
)
export default class AuthContainer extends Component {

  _handleLoginCancel = (e) => {
    const { auth, authActions, pushPath } = this.props;
    pushPath(auth.prevRoute); // from our saved state value
    authActions.closeLogin();
  };

  _handleLoginSubmit = (e) => {
    const { authActions } = this.props;
    // do whatever you need to login here
    authActions.closeLogin();
  };

  render() {
    const { auth } = this.props;
    return (
      <div>
        <LoginDialog
          open={auth.showLogin}
          handleCancel={this._handleLoginCancel}
          handleSubmit={this._handleLoginSubmit}
          submitLabel="Login"
        />
       ...
      </div>
    )
  }
}

我显然在使用 ES6、Babel 和 webpack...但是这些原则应该在没有它们的情况下适用,因为不应该使用 Redux(您可以将 prev 路由存储在本地存储或其他东西中)。此外,为了简洁起见,我省略了一些传递道具的中间组件。

其中一些可能是功能组件,但我将它们保留完整以显示更多细节。通过抽象其中的一些,也有一些改进的空间,但我再次将其多余以显示更多细节。希望这对您有所帮助!