如何将路由器中的组件状态传递给另一个路由器组件

时间:2017-12-10 16:00:22

标签: javascript reactjs

我现在正在设计frontend部分。这是我的Router
/是登录页面。它会检查token旁边的localStorage。它在isAuthenticated

中有state

/select-teams是功能页面,在让用户使用之前,它必须首先验证token
问题是。
1.每次token query之前,应该每次刷新APIs吗? 2.每次VS通过状态时,在实现componentWillMount和检查token函数之间。哪一个是最佳实践?每次IMO阅读localStorage可能会减慢应用程序的速度。但是,我很容易做到,但不是DRY 3.假设我想在isAuthenticated之间将Route状态传递给另一个Route。我怎么能这样做?

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import registerServiceWorker from './registerServiceWorker';
import {Provider} from 'react-redux';
import {applyMiddleware, createStore} from 'redux';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import promise from 'redux-promise';
import LoginPage from "./components/loginPage";
import reducers from './reducers/index';
import SelectTeam from './components/select_teams';

const createStoreWithMiddleware = applyMiddleware(promise)(createStore);

ReactDOM.render(
  <Provider store={createStoreWithMiddleware(reducers)}>
    <BrowserRouter>
      <div>
        <Switch>
          <Route path="/select-teams" component={SelectTeam}/>
          <Route path="/" component={LoginPage}/>
        </Switch>
      </div>
    </BrowserRouter>
  </Provider>
  , document.getElementById('root'));
registerServiceWorker();

这是我的Component

import React, {Component} from 'react';
import {connect} from 'react-redux';
import login_image from '../images/login_image.png';
import {Field, reduxForm} from 'redux-form';
import {getTokenAuth, refreshToken} from "../actions";
import ErrorMessage from './errorMessage';

class LoginPage extends Component {
  //1. Verify user token. If token is good.
  //1.1 Do refresh token and
  //1.2 Then put him to `select-teams` page
  //1.3 Finally set isAuthenticated: true

  //2. If token is not good. Let user login again.
  //By setting the isAuthenticated: false
  constructor(props) {
    super(props);
    const token = localStorage.getItem('token');
    const isAuthenticated = !((token === undefined) | (token === null));
    this.state = {
      token: localStorage.getItem('token'),
      isAuthenticated,
      message: null,
      statusCode: null
    };
  }

  componentWillMount() {
    console.log('Enter componentWillMount');
    const token = this.state.token;
    if (token === undefined || token === null)
      this.setState((prevState) => {
        {
          isAuthenticated: false
        }
      });
    else {
      console.log('componentWillMount token is exist');
      //Refresh the token. Let backend verify it
      //Outcome is 1. It is expired and unable to refresh
      //Or 2. It is refreshed
      this.props.refreshToken(token, (res) => {
        console.log(res.status);
        if (res.status === 200) {
          //Receive new token
          localStorage.setItem('token', res.data.token);
          this.setState((prevState) => {
            return Object.assign(prevState, {
              isAuthenticated: true,
              statusCode: res.status,
              message: res.statusText
            });
          });
          this.props.history.push('/select-teams');
        } else {
          //Token is expired and can not be able to refresh again. Force user to do login again by remove his token
          localStorage.removeItem('token');
          this.setState((prevState) => {
            return Object.assign(prevState, {
              isAuthenticated: false,
              statusCode: res.status,
              message: res.data.non_field_errors
            });
          });
        }
      })
    }
  }

  renderField(field) {
    const {meta: {touched, error}} = field;
    const className = `'form-group' ${touched && error ? 'has-danger' : ''}`;

    return (
      <div className={className}>
        <label>{field.label}</label>
        <input
          className="form-control"
          type={field.type}
          placeholder={field.placeholder}
          {...field.input}
        />
        <div className="text-help">
          {touched ? error : ''}
        </div>
      </div>
    );
  }


  onSubmit(values) {
    console.log(values);
    this.props.getTokenAuth(values, (res) => {
      console.log(res.status);
      if (res.status === 200) {
        localStorage.setItem('token', res.data.token);
        this.setState((prevState) => {
          return Object.assign(prevState, {
            isAuthenticated: true,
            statusCode: res.status,
            message: res.statusText
          });
        });
        this.props.history.push('/select-teams');
      } else {
        localStorage.removeItem('token');
        this.setState((prevState) => {
          return Object.assign(prevState, {
            isAuthenticated: false,
            statusCode: res.status,
            message: res.data.non_field_errors
          });
        });
      }
    })
  }

  render() {
    const {handleSubmit} = this.props;

    return (
      <div>
        <img src={login_image} alt="Poink Logo"/>
        <ErrorMessage
          isAuthenticated={this.state.isAuthenticated}
          message={this.state.message}
        />

        <form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
          <Field
            name="userid"
            component={this.renderField}
            placeholder="User ID"
            type="text"
          />
          <Field
            name="password"
            component={this.renderField}
            placeholder="Password"
            type="password"
          />
          <button type="submit" className="btn btn-primary">Submit</button>
        </form>
        <a className='btn btn-primary' href="https://www.magicboxasia.com/">Sign up</a>
      </div>
    );
  }
}

function validate(values) {
  const errors = {};

  // Validate the inputs from 'values'
  if (!values.userid) {
    errors.userid = "Enter a user ID!";
  }

  if (!values.password) {
    errors.password = "Enter your password";
  }

  return errors;
}

function mapStateToProps(state) {
  return {
    token: state.token,
    isAuthenticated: state.isAuthenticated,
  }
}

export default reduxForm({
  validate,
  form: 'LoginForm'
})(
  connect(mapStateToProps, {getTokenAuth, refreshToken})(LoginPage)
);

1 个答案:

答案 0 :(得分:1)

您的反应应用程序应验证令牌是否在本地存储/另一个存储上,并且您的后端必须验证令牌是否有效,因为您无法验证令牌是否有效而没有其秘密。

我们在我工作的公司所做的是一个高阶组件,它接收一个函数,检查令牌是否存储为道具。如果函数返回true,我们渲染组件,如果它是dosnt,它会重定向到登录路由。该函数称为isAuthorized

const PrivateRoute = ({ component: Component, isAuthorized, ...otherProps }) => (
<Route
    {...otherProps}
    render={props => (
        isAuthorized() ? (<Component {...props} />) :
            (
                <Redirect to={
                    {
                        pathname: '/login',
                        state: { from: props.location },
                    }
                }
                />
            )
    )}
/>
);

这是我们的索引。我们导入的函数告诉用户是否有权查看此路由,在这种情况下是hasToken

<Switch>
    <PrivateRoute exact path="/" isAuthorized={hasToken} component={Home} />
    <PrivateRoute exact path="/list" isAuthorized={hasToken} component={List} />
    <PrivateRoute exact path="/view/:id" isAuthorized={hasToken} component={View} />
    <Route component={PageNotFound} />
</Switch>

现在,如果令牌不存在,它的PrivateRoute重定向可以重定向用户登录。

现在,如果后端返回401(未经授权),您应该研究一种将用户重定向到登录的好方法。后端是实际的令牌验证器。在我工作的公司中,我们使用axios来执行ajax调用,并且我们创建了一个axios拦截器,如果后端响应返回401 http状态代码,则会将用户重定向到登录路由。