setState(...):在现有状态转换期间无法更新(例如在`render`或其他组件的构造函数中)

时间:2017-08-21 09:09:32

标签: javascript reactjs

我有如下组件:

const VIEW_PROFILE = 'profile'
const VIEW_SAA = 'saa'
const VIEW_DOCUMENTS = 'documents'
const VIEW_FINANCIERS = 'viewFinancierInfo'

class ViewFinancier extends Component {
  constructor(props) {
    super(props)
    this.state = {
      selectedNavigation: VIEW_PROFILE,
      financierId: this.props.location.state.financierDetails.financierId,
      defaultLoading: false
    }

    this.handleChangeNav = this.handleChangeNav.bind(this)
    this.handleCancelButton = this.handleCancelButton.bind(this)
  }

  componentWillMount(props) {
    this.props.loadFinancierProfile(this.props.location.state.financierDetails.financierId)
  }

  componentWillReceiveProps(newProps) {
    this.setState({
      defaultLoading: true,
      viewfinancierprofiledata: newProps.viewfinancierprofiledata
    })
  }

  handleChangeNav(e) {
    var selectedNavigation = e.target.attributes.getNamedItem('value').value
    this.setState({
      selectedNavigation: selectedNavigation
    })
  }

  handleCancelButton(changingState) {
    this.setState({
      selectedNavigation: changingState
    })
  }

  render() {
    if (this.state.defaultLoading === false) {
      return (
        <div className="tabledataloading tabletext">
          Please wait while the data is loading <img alt="Loading..." src={loadingimg} />
        </div>
      )
    } else if (this.state.selectedNavigation === VIEW_FINANCIERS) {
      this.props.history.push('/financier/')
      return null
    } else {
      return (
        <div id="lesse-info-component-wrapper" className="melody-common-container">
          <div id="header-wrapper">
            <h1 className="melody-common-module-title">VIEW FINANCIER INFO</h1>
          </div>
          <div id="navigation-wrapper">
            <ul id="add-financier-info-nav" className="topnavpad">
              <li
                value={VIEW_PROFILE}
                onClick={this.handleChangeNav}
                className={'add-financier-info-nav-item ' + (this.state.selectedNavigation === VIEW_PROFILE ? 'active' : '')}>
                PROFILE
              </li>
              <li
                value={VIEW_SAA}
                onClick={this.handleChangeNav}
                className={'add-financier-info-nav-item ' + (this.state.selectedNavigation === VIEW_SAA ? 'active' : '')}>
                SAA
              </li>
              <li
                value={VIEW_DOCUMENTS}
                onClick={this.handleChangeNav}
                className={'add-financier-info-nav-item ' + (this.state.selectedNavigation === VIEW_DOCUMENTS ? 'active' : '')}>
                DOCUMENTS
              </li>
            </ul>
          </div>
          {this.state.selectedNavigation === VIEW_PROFILE
            ? <ViewFinancierProfile financierId={this.props.financierId} onCancelHandler={this.handleCancelButton} />
            : null}
          {this.state.selectedNavigation === VIEW_SAA
            ? <ViewFinancierSAA financierId={this.props.financierId} onCancelHandler={this.handleCancelButton} />
            : null}
          {this.state.selectedNavigation === VIEW_DOCUMENTS
            ? <ViewFinancierDocument financierId={this.props.financierId} onCancelHandler={this.handleCancelButton} />
            : null}
        </div>
      )
    }
  }
}

const mapStateToProps = state => {
  return {
    viewfinancierprofiledata: state.viewfinancierprofiledata
  }
}

const mapDispatchToProps = dispatch => {
  return {
    loadFinancierProfile: financierId => dispatch(viewFinancierProfileAction.loadFinancierProfile(financierId))
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ViewFinancier)

执行时我收到警告:Warning: setState(...): Cannot update during an existing state transition (such as within渲染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`

在查看控制台时,我发现代码段this.props.history.push('/financier/')正在抛出错误。

关于Stack Overflow中其他类似问题的进一步研究,我还发现这实际上是在render方法下设置父状态,这是不允许的。

但现在我无法弄清楚如何才能达到我想要的条件。

1 个答案:

答案 0 :(得分:1)

组件props不应该自行变异。我认为在这种情况下有两种解决方案。

  1. this.props.history复制到state,并通过调用history变更setState。将selectedNavigation的检查置于render之外。例如(只是我的想法,请根据您的应用程序逻辑修改它):
  2. class ViewFinancier extends Component {
      constructor(props) {
        super(props)
        this.state = {
          ...
          history = props.history,
          ...
        }
        ...
      }
    
      componentWillReceiveProps(newProps) {
        this.setState({
          history: newProps.history,
          ...
        })
      }
      
      handleChangeNav(e) {
        var selectedNavigation = e.target.attributes.getNamedItem('value').value;
        
        this.setState({
          selectedNavigation: selectedNavigation      
        })
        
        this.historyCheck(selectedNavigation);
      }
    
      handleCancelButton(changingState) {
        this.setState({
          selectedNavigation: changingState
        });
        
        this.historyCheck(changingState);
      }
      
      historyCheck(nav) {
        if (nav === VIEW_FINANCIERS) {
          history.push('/financier/');
        };
        
        this.setState(history: history);
      }
    
      render() {    
        ...
        } else if (this.state.selectedNavigation === VIEW_FINANCIERS) {
          return null
        }
        ...
      }
    }

    1. 如果父组件也使用this.props.history。然后,您可以传递一个函数来更新父组件中的history,并将此函数传递给当前(子)组件。
    2. class ParentFinancier extends Component {
        pushHistory => (val) {
          let {history} = this.state;
          this.setState(history: history.push('/financier/'));
        }
        
        render() {
          ...
          <ViewFinancier pushHistory={this.pushHistory} ... />
        }
        ...
      }
      
      
      
      class ViewFinancier extends Component {
        ...
        } else if (this.state.selectedNavigation === VIEW_FINANCIERS) {
          this.props.pushHistory('/financier/')
          return null   
        }
        ...
      }
      
      ...