重定向后React-router-redux setState警告

时间:2017-03-18 09:52:40

标签: javascript reactjs react-router react-redux react-router-redux

我正在使用react,redux,react-routerreact-router-redux为项目构建管理员应用。 React-router是v4.0.0,react-router-redux是v5.0.0-alpha.3(与npm install react-router-redux@next一起安装)。我正在尝试的是:

  1. 加载应用
  2. 对后端执行异步调用以查看用户是否已登录(存储在cookie中的令牌),
  3. 如果用户未登录,请重定向到/login并呈现Login组件。
  4. 对于我正在使用redux-thunk的异步操作。

    Root.js

    import React, { Component, PropTypes } from 'react';
    import { Provider, connect } from 'react-redux';
    import { Route, Switch } from 'react-router-dom';
    import { ConnectedRouter, push } from 'react-router-redux';
    
    import Login from './Login';
    
    const App = () => <h1>Dashboard</h1>;
    const NotFound = () => <h1>Not found :(</h1>;
    
    class Root extends Component {
    
      // use componentDidMount as recommended here:
      // https://facebook.github.io/react/docs/react-component.html#componentdidmount
      componentDidMount() {
        const { dispatch, user } = this.props;
        if (!user) {
          dispatch(push('/login'));
        }
      }
    
      render() {
        const { store, history } = this.props;
        return (
          <Provider store={store}>
            <ConnectedRouter history={history}>
              <div>
                <Switch>
                  <Route exact path='/' component={App} />
                  <Route exact path='/login' component={Login} />
                  <Route component={NotFound} />
                </Switch>
              </div>
            </ConnectedRouter>
          </Provider>
        );
      }
    }
    
    Root.propTypes = {
      store: PropTypes.object.isRequired,
      history: PropTypes.object.isRequired,
      dispatch: PropTypes.func.isRequired,
      user: PropTypes.shape({
        email: PropTypes.string.isRequired
      })
    };
    
    const mapStateToProps = state => ({
      ready: state.ready,
      user: state.user
    });
    
    export default connect(mapStateToProps)(Root);
    

    Login.js

    import React, { Component, PropTypes } from 'react';
    import { connect } from 'react-redux';
    
    import {
      loginFormChange,
      loginFormSubmit
    } from '../actions';
    
    class Login extends Component {
      constructor(props) {
        super(props);
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
      }
    
      handleChange(event) {
        const { target } = event,
          { value, name } =  target,
          { dispatch } = this.props;
        dispatch(loginFormChange({
          [name]: value
        }));
      }
    
      handleSubmit(event) {
        event.preventDefault();
        const { dispatch, login } = this.props,
          { email, password } =  login;
        dispatch(loginFormSubmit({
          email,
          password
        }));
      }
    
      render() {
        const { login } = this.props,
          { email, password } = login;
        return (
          <form onSubmit={this.handleSubmit}>
            <input type="email" name="email" value={email} onChange={this.handleChange} />
            <input type="password" name="password" value={password} onChange={this.handleChange} />
            <button type="submit">Sign in</button>
          </form>
        );
      }
    }
    
    Login.propTypes = {
      dispatch: PropTypes.func.isRequired,
      login: PropTypes.shape({
        email: PropTypes.string.isRequired,
        password: PropTypes.string.isRequired
      }).isRequired
    };
    
    const mapStateToProps = state => ({
      login: state.login
    });
    
    export default connect(mapStateToProps)(Login);
    

    actions.js

    export const LOGIN_FORM_CHANGE = 'Login form change';
    export const LOGIN_FORM_SUBMIT = 'Login form submit';
    export const AUTHENTICATE_USER = 'Authenticate user';
    
    export const loginFormChange = data => {
      const { email, password } = data;
      return {
        type: LOGIN_FORM_CHANGE,
        email,
        password
      };
    };
    
    export const loginFormSubmit = data => dispatch => {
      const { email, password } = data;
      return fetch('/api/auth/token', {
        headers: {
          'Authorization': 'Basic ' + btoa([ email, password ].join(':'))
        },
        credentials: 'same-origin'
      })
        .then(response => {
          if (!response.ok) {
            throw new Error(response.statusText);
          }
          return response.json();
        })
        .then(user => {
          // this line will throw setState warning:
          // Warning: setState(...): Cannot update during an existing state transition (such as within `render` or another component's constructor). Render methods should be a pure function of props and state; constructor side-effects are an anti-pattern, but can be moved to `componentWillMount`.
          dispatch(authenticateUser(user));
        });
    };
    
    export const authenticateUser = data => {
      const { email } = data;
      return {
        type: AUTHENTICATE_USER,
        email
      };
    };
    

    我想指出我正在使用建议的异步操作方法,描述in redux documentation。为简洁起见,我不会发布我的减速机。最后:

    index.js

    import React from 'react';
    import { render } from 'react-dom';
    import { createStore, applyMiddleware } from 'redux';
    import createHistory from 'history/createBrowserHistory';
    import { routerMiddleware } from 'react-router-redux';
    import thunk from 'redux-thunk';
    import createLogger from 'redux-logger';
    
    import reducers from './reducers';
    import Root from './containers/Root';
    
    const history = createHistory(),
      middleware = [
        routerMiddleware(history),
        thunk
      ];
    
    if (process.env.NODE_ENV !== 'production') {
      middleware.push(createLogger());
    }
    
    const store = createStore(
      reducers,
      applyMiddleware(...middleware)
    );
    
    render(
      <Root store={store} history={history} />,
      document.getElementsById('root')
    );
    

    因此,在loginFormSubmit异步操作尝试发送同步 authenticateUser操作时,警告会被抛出。此外,它只在重定向后发生。我尝试过不同的重定向方法:

      来自react-router-redux的
    • push
    • 来自react-router的
    • Redirect组件

    我还尝试将重定向调用放在不同的地方(componentWillMountcomponentDidMountcomponentWillReceiveProps,在组件内部进行条件渲染,使用条件PrivateRoute组件在react-router documentation等中描述,但似乎没有任何效果。

    如果首先没有重定向(例如,用户打开/login页面而不是受保护的页面),则没有警告。

    非常感谢任何有关此问题的帮助。

1 个答案:

答案 0 :(得分:5)

我遇到了同样的问题,基本上它是来自react-router-redux v5.0.0-alpha.2和alpha.3的ConnectedRouter的错误

过去几天它一直在积极讨论,但现在它已修复为alpha 4并且问题已经解决:

https://github.com/ReactTraining/react-router/issues/4713