构造函数被称为两次反应组件



我的React组件的构造函数被调用两次,但我不知道原因。我正在使用React-Redux存储我的应用程序的语言。我的功能可以在浏览器的语言上设置默认语言基础。登录页是第一个获得渲染的组件,因此我在其构造函数中调用了我的函数。基本上,它的作用是比较和派遣动作。当我使用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;

首先,您不应从构造函数中调用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);

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

history.push('/');

这将两次渲染组件。

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

"生命周期不会在生产模式下进行双重击退。

是的。这是有意的,并在这里记录了。它仅在开发模式下发生。

如果您检查了正在渲染的内容的react dev工具,您会看到诸如 connect(App)以及 App的内容,因此您的console.log将被调用两次。这是因为连接是一个高阶组件。

如果您移动this.setDefaultLanguage()(和您的console.log,以便您可以在构造函数中看到它),然后进入componentDidMount生命周期挂钩,它应该解决您的问题。

解决方案:替换&quot;&quot"&lt;&gt;&quot;在您的index.js

**原因:**重要的是,状态和生命周期方法不包含副作用。忽略此规则可能会导致各种问题,包括内存泄漏和无效的应用程序状态。不幸的是,很难检测到这些问题,因为它们通常是不确定性的。

严格的模式无法自动为您检测副作用,但是它可以通过使它们更确定性来帮助您发现它们。这是通过有意双击击功能来完成的:

  1. 构造函数
  2. 渲染
  3. showscomponentupdate
  4. GetDerivedStateFromprops
  5. 功能组件
  6. 状态更新程序功能(SetState的第一个参数)
  7. 功能传递给Usestate,Usememo或用户ducer

这仅适用于开发模式。生命周期不会在生产模式下进行双重击退。

可以使用以下代码在 index.js

中禁用开发模式
//  <React.StrictMode>
    <Application />
//  </React.StrictMode>

最新更新