【问题标题】:Constructor getting called twice React Component构造函数被调用两次 React 组件
【发布时间】:2018-05-18 13:39:42
【问题描述】:

我的 react 组件的构造函数被调用了两次,但我不知道为什么。我正在使用 react-redux 来存储我的应用程序的语言。我有一个功能可以根据浏览器的语言设置默认语言。 LoginPage 是第一个渲染的组件,所以我在其构造函数中调用了我的函数。基本上它所做的是比较和调度一个动作。当我使用 redux 开发人员工具检查我的状态时,我看到它被分派了两次。我在构造函数中打印了虚拟数据,它也被打印了两次。

LoginPage.js

import React from 'react';
import {connect} from 'react-redux';
import {startLogin} from '../actions/auth';
import {setLanguage} from '../actions/lang';

export class LoginPage extends React.Component{

  constructor(props){
    super(props);
    this.setDefaultLanguage();
    console.log('i am constructor');
  }

  changeLanguage = (e) => {
    const lan = e.target.value;
    this.props.setLanguage(lan);
  };

  setDefaultLanguage = () => {
    const defaultLanguage = navigator.language || navigator.userLanguage || 'en-US';

    if(defaultLanguage == 'es'){
      this.props.setLanguage(defaultLanguage);
    }else{
      this.props.setLanguage('en');
    }
  }

  render(){
    return(
      <div className="box-layout">
        <div className="box-layout__box">
          <h1 className="box-layout__title">Expensify</h1>
          <p>It\'s time to get your expenses under control.</p>
          <button className="button" onClick={this.props.startLogin}>Log in with google</button>
          <select className="select" onChange={this.changeLanguage}>
            <option value="en">English</option>
            <option value="es">Español</option>
          </select>
        </div>
      </div>
    )
  };
}

const mapDispatchToProps = (dispatch) => ({
  startLogin: () => dispatch(startLogin()),
  setLanguage: (language) => dispatch(setLanguage(language))
});

export default connect(undefined, mapDispatchToProps)( LoginPage);

App.js

import React from 'react';
import ReactDom from 'react-dom';
import {Router, Route, Switch} from 'react-router-dom';
import createHistory from 'history/createBrowserHistory';
import ExpenseDashBoardPage from '../components/ExpenseDashBoardPage';
import AddExpensePage from '../components/AddExpensePage';
import EditExpensePage from '../components/EditExpensePage';
import NotFoundPage from '../components/NotFoundPage';
import LoginPage from '../components/LoginPage';
import PrivateRoute from './PrivateRoute';
import PublicRoute from './PublicRoute';

export const history = createHistory();

const AppRouter = () => (
  <Router history={history}>
    <div>
      <Switch>
        <PublicRoute path="/" component={LoginPage} exact={true} />
        <PrivateRoute path="/dashboard" component={ExpenseDashBoardPage} />
        <PrivateRoute path="/create" component={AddExpensePage} />
        <PrivateRoute path="/edit/:id" component={EditExpensePage} />
        <Route component={NotFoundPage} />
      </Switch>
    </div>

  </Router>
)

export default AppRouter;

【问题讨论】:

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


【解决方案1】:

如果您检查 React Dev Tools 正在呈现的内容,您会看到 connect(App) 以及 App 本身,因此您的 console.log 将被调用两次。这是因为 connect 是一个高阶组件。

如果您将this.setDefaultLanguage()(以及您的console.log,以便您可以看到它在运行中)从构造函数中移出并进入componentDidMount 生命周期挂钩,它应该可以解决您的问题。

【讨论】:

    【解决方案2】:

    首先,您不应该在构造函数中调用 Redux 操作或任何类型的 AJAX。这些事情应该在componentDidMount()完成。

    其次,我会向商店请求语言作为道具的一部分。如果未定义,则在componentDidMount() 中调用您的setDefaultLanguage()

    我至少会做以下事情:

    export class LoginPage extends React.Component {
      componentDidMount() {
        if (!this.props.lang) {
            this.setDefaultLanguage();
        }
      }
    
      changeLanguage = (e) => {
        const lan = e.target.value;
        this.props.setLanguage(lan);
      };
    
      setDefaultLanguage = () => {
        const defaultLanguage = navigator.language || navigator.userLanguage || 'en-US';
    
        if(defaultLanguage == 'es'){
          this.props.setLanguage(defaultLanguage);
        }else{
          this.props.setLanguage('en');
        }
      }
    
      render() {
        return(
          <div className="box-layout">
            <div className="box-layout__box">
              <h1 className="box-layout__title">Expensify</h1>
              <p>It\'s time to get your expenses under control.</p>
              <button className="button" onClick={this.props.startLogin}>Log in with google</button>
              <select className="select" onChange={this.changeLanguage}>
                <option value="en">English</option>
                <option value="es">Español</option>
              </select>
            </div>
          </div>
        )
      };
    }
    
    const mapStateToProps = state => ({
        // Assuming `steate.lang` is where you would set the language.
        lang: state.lang
    });
    
    const mapDispatchToProps = (dispatch) => ({
      startLogin: () => dispatch(startLogin()),
      setLanguage: (language) => dispatch(setLanguage(language))
    });
    
    const mergeProps = (stateProps, dispatchProps) => ({ ...stateProps, ...dispatchProps });
    
    export default connect(mapStateToProps, mapDispatchToProps, mergeProps)(LoginPage);
    

    【讨论】:

    【解决方案3】:

    我遇到了同样的问题,因为我在我的公共路由中重定向了用户。所以它渲染了两次组件。如果您正在使用

    history.push('/');
    

    这将渲染组件两次。

    【讨论】:

      【解决方案4】:

      这两次调用一些生命周期方法,仅用于开发模式。

      “生产模式下不会重复调用生命周期。”

      【讨论】:

        【解决方案5】:

        是的。这是故意的,并在此处记录。它只发生在开发模式下。

        https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects

        【讨论】:

          猜你喜欢
          • 2017-04-02
          • 1970-01-01
          • 2012-06-11
          • 1970-01-01
          • 1970-01-01
          • 2019-12-02
          • 1970-01-01
          • 2013-12-11
          相关资源
          最近更新 更多