setState触发构造函数

时间:2017-07-22 08:23:25

标签: javascript reactjs

几周左右我就开始学习一些教程来学习一些React。

现在我试图让Auth使用管理信息中心。 我在仪表板中有一个侧边栏,希望它切换。在我添加Auth之前,这个工作正常。 现在我有一个HOC:

import React, {Component} from 'react';
import {connect} from 'react-redux';
import {Route, Redirect} from 'react-router-dom';

export const PrivateRoute = ({component: ComposedComponent, ...rest}) => {

  class Authentication extends Component {

    // redirect if not authenticated; otherwise, return the component inputted into <PrivateRoute />
    handleRender(props) {
      if (!this.props.authenticated) {
        return <Redirect to={{
          pathname: '/login',
        }}/>;
      } else {
        return <ComposedComponent {...props}/>;
      }
    }

    render() {
      return (
          <Route {...rest} render={this.handleRender.bind(this)}/>
      );
    }
  }

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

  const AuthenticationContainer = connect(mapStateToProps)(Authentication);
  return <AuthenticationContainer/>;
};

我的TemplateComponent

import React, {Component} from 'react';
import './Template.css';

class Template extends Component {

  constructor(props) {
    super(props)
    this.state = {toggled: true}
  }

  toggleMenu = () => {
    this.setState({toggled: !this.state.toggled});
  };

  render() {
    const sidebar = `sidebar-toggle ${this.state.toggled ? 'active' : ''}`;
    return(
        <nav className="navbar navbar-inverse" role="navigation">
          <div className="container">
            <div className="navbar-header">
              <a id="menu-toggle" href="#" className="navbar-toggle" onClick={this.toggleMenu}>
                <span className="sr-only">Toggle navigation</span>
                <span className="icon-bar"/>
                <span className="icon-bar"/>
                <span className="icon-bar"/>
              </a>
              <a className="navbar-brand" href="home.xhtml">
                Araido
              </a>
            </div>
            <div id="sidebar-wrapper" className={sidebar} >
              <ul className="sidebar-nav">
                <li>
                  <a href="#item1">Item 1</a>
                </li>
                <li>
                  <a href="#item2">Item 2</a>
                </li>
                <li>
                  <a href="#item3">Item 3</a>
                </li>
              </ul>
            </div>
          </div>
        </nav>
    )
  }
}

export default Template;

现在发生的事情是当我的组件使用setState()时它会再次触发构造函数。这使我的侧边栏状态恢复为切换状态。

提前致谢

1 个答案:

答案 0 :(得分:0)

HOC被描述为

  

高阶组件是一个带有组件的函数   返回一个新组件。

来源:Higher-Order Components - React Documentation

您的HOC实例化反应组件而不是仅返回它。我想说更常见的模式是返回一个新组件,而不是立即在HOC中创建新实例。

这可能是一个潜在的问题,但它没有必要,因为它取决于您的HOC如何使用组合组件。

仍然可以尝试更改此行

return <AuthenticationContainer/>;
// which is equal to
return React.createElement(AuthenticationContainer, null);

return AuthenticationContainer;

请记住,它可能无法解决您的问题。您只共享了应用程序代码的一小部分,但我确信100%您正在努力解决官方React文档中描述的问题 - Don't Use HOCs Inside the render Method

  

[...]重新安装组件会导致该组件的状态   所有的孩子都要迷失。

您必须找到以错误方式使用HOC的位置。查看显示此问题的示例:DEMO

单击Toggle按钮并打开控制台,您将看到父组件中的每个setState()调用都会重新装入FooWithHello并调用其构造函数。

其他改进#1

避免在渲染方法中创建新函数,而不是在.bind()中使用render()

<Route {...rest} render={this.handleRender.bind(this)}/>

你应该做

this.handleRender = () => {
  if (!this.props.authenticated) {
    return <Redirect to={{ pathname: '/login' }} />;
  }

  return <ComposedComponent {...props} />;
};

render() {
  return <Route {...rest} render={this.handleRender} />;
}

其他改进#2

当你想使用以前的状态来构建下一个状态时,你应该使用setState()的回调形式:

toggleMenu = () => {
  this.setState(prevState => ({
    toggled: !prevState.toggled,
  });
};