【问题标题】:ReactJS: Warning: setState(...): Cannot update during an existing state transitionReactJS:警告:setState(...):在现有状态转换期间无法更新
【发布时间】:2016-09-20 02:51:38
【问题描述】:

我正在尝试从我的渲染视图重构以下代码:

<Button href="#" active={!this.state.singleJourney} onClick={this.handleButtonChange.bind(this,false)} >Retour</Button>

到绑定在构造函数中的版本。原因是渲染视图中的绑定会给我带来性能问题,尤其是在低端手机上。

我创建了以下代码,但我不断收到以下错误(很多)。看起来应用程序陷入了循环:

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`.

下面是我使用的代码:

var React = require('react');
var ButtonGroup = require('react-bootstrap/lib/ButtonGroup');
var Button = require('react-bootstrap/lib/Button');
var Form = require('react-bootstrap/lib/Form');
var FormGroup = require('react-bootstrap/lib/FormGroup');
var Well = require('react-bootstrap/lib/Well');

export default class Search extends React.Component {

    constructor() {
        super();

        this.state = {
            singleJourney: false
        };

        this.handleButtonChange = this.handleButtonChange.bind(this);
    }

    handleButtonChange(value) {
        this.setState({
            singleJourney: value
        });
    }

    render() {

        return (
            <Form>

                <Well style={wellStyle}>

                    <FormGroup className="text-center">

                        <ButtonGroup>
                            <Button href="#" active={!this.state.singleJourney} onClick={this.handleButtonChange(false)} >Retour</Button>
                            <Button href="#" active={this.state.singleJourney} onClick={this.handleButtonChange(true)} >Single Journey</Button>
                        </ButtonGroup>
                    </FormGroup>

                </Well>

            </Form>
        );
    }
}

module.exports = Search;

【问题讨论】:

标签: reactjs constructor setstate


【解决方案1】:

看起来您在渲染方法中不小心调用了handleButtonChange 方法,您可能想要改为使用onClick={() =&gt; this.handleButtonChange(false)}

如果您不想在 onClick 处理程序中创建 lambda,我认为您需要有两个绑定方法,每个参数一个。

constructor

this.handleButtonChangeRetour = this.handleButtonChange.bind(this, true);
this.handleButtonChangeSingle = this.handleButtonChange.bind(this, false);

render 方法中:

<Button href="#" active={!this.state.singleJourney} onClick={this.handleButtonChangeSingle} >Retour</Button>
<Button href="#" active={this.state.singleJourney} onClick={this.handleButtonChangeRetour}>Single Journey</Button>

【讨论】:

  • 我已经尝试过该解决方案,并且它有效。但我不认为这个在线解决方案是首选解决方案。我想要完成的是我的绑定在渲染视图之外。在这个解决方案中, this.handleButtonChange = this.handleButtonChange.bind(this);在构造函数中不再需要,这是否意味着我的绑定再次在我的渲染视图中?
  • 可能发生的情况是当我设置活动状态时,它会触发onClick,从而导致循环。有什么方法可以在不触发 onClick 的情况下设置活动状态?
  • 为什么 lambda 是解决方案?这背后是什么概念?我看不到。
  • 主要区别在于 onClick={this.handleButtonChange(false)} 和 onClick={() => this.handleButtonChange(false)} - 第一个是错误的,因为它只是调用handleButtonChange 方法立即将其返回值(未定义)分配给 onClick 句柄 - 因此没有任何反应。第二个实际上为 onClick 分配了一个方法 - 一个调用 handleButtonChange。
  • @VladimirRovensky 非常感谢你,我想知道你的救生员
【解决方案2】:

我给出一个通用的例子以便更好地理解,在下面的代码中

render(){
    return(
      <div>

        <h3>Simple Counter</h3>
        <Counter
          value={this.props.counter}
          onIncrement={this.props.increment()} <------ calling the function
          onDecrement={this.props.decrement()} <-----------
          onIncrementAsync={this.props.incrementAsync()} />
      </div>
    )
  }

提供道具时,我直接调用函数,这将执行无限循环,并会给您该错误,删除函数调用一切正常。

render(){
    return(
      <div>

        <h3>Simple Counter</h3>
        <Counter
          value={this.props.counter}
          onIncrement={this.props.increment} <------ function call removed
          onDecrement={this.props.decrement} <-----------
          onIncrementAsync={this.props.incrementAsync} />
      </div>
    )
  }

【讨论】:

  • 难怪当时我遇到了无限循环异常。傻我。之后,我在constructor 上使用bind 按钮和箭头功能()=&gt;
【解决方案3】:

这通常发生在你打电话时

onClick={this.handleButton()} - 注意 () 而不是:

onClick={this.handleButton} - 注意这里我们在初始化函数时并没有调用它

【讨论】:

    【解决方案4】:

    问题在这里:onClick={this.handleButtonChange(false)}

    当您将this.handleButtonChange(false) 传递给onClick 时,您实际上是在使用value = false 调用函数并将onClick 设置为函数的返回值,这是未定义的。此外,调用this.handleButtonChange(false) 然后调用this.setState() 会触发重新渲染,从而导致无限渲染循环。

    解决方案是在 lambda 中传递函数:onClick={() =&gt; this.handleButtonChange(false)}。在这里,您将 onClick 设置为等于单击按钮时将调用 handleButtonChange(false) 的函数。

    下面的例子可能会有所帮助:

    function handleButtonChange(value){
      console.log("State updated!")
    }
    
    console.log(handleButtonChange(false))
    //output: State updated!
    //output: undefined
    
    console.log(() => handleButtonChange(false))
    //output: ()=>{handleButtonChange(false);}
    

    【讨论】:

      【解决方案5】:

      如果您尝试向recompose 中的处理程序添加参数,请确保您在处理程序中正确定义了参数。它本质上是一个柯里化函数,因此您要确保需要正确数量的参数。 This page has a good example of using arguments with handlers.

      示例(来自链接):

      withHandlers({
        handleClick: props => (value1, value2) => event => {
          console.log(event)
          alert(value1 + ' was clicked!')
          props.doSomething(value2)
        },
      })
      

      为您的孩子 HOC 和在父母中

      class MyComponent extends Component {
        static propTypes = {
          handleClick: PropTypes.func, 
        }
        render () {
          const {handleClick} = this.props
          return (
            <div onClick={handleClick(value1, value2)} />
          )
        }
      }
      

      这避免了从你的处理程序中编写一个匿名函数来修补解决在你的处理程序上没有提供足够的参数名称的问题。

      【讨论】:

        【解决方案6】:

        来自反应文档Passing arguments to event handlers

        <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
        <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
        

        【讨论】:

          【解决方案7】:

          render() 调用中进行的任何状态更改都会发出同样的警告。

          一个难以找到的案例的例子: 在基于状态数据渲染多选 GUI 组件时,如果状态没有可显示的内容,则对 resetOptions() 的调用被视为该组件的状态更改。

          明显的解决方法是在componentDidUpdate() 中使用resetOptions() 而不是render()

          【讨论】:

            【解决方案8】:

            我打电话时遇到同样的错误

            this.handleClick = this.handleClick.bind(this);
            

            handleClick 不存在时在我的构造函数中

            (我已经删除了它,并且不小心将“this”绑定语句留在了我的构造函数中)。

            解决方案 = 删除“this”绑定语句。

            【讨论】:

              【解决方案9】:

              问题肯定是在使用 onClick 处理程序渲染按钮时的 this 绑定。解决方案是在渲染时调用动作处理程序时使用箭头函数。像这样: onClick={ () =&gt; this.handleButtonChange(false) }

              【讨论】:

              • 好主意! :)
              【解决方案10】:

              我用来为组件打开Popover的解决方案是reactstrap (React Bootstrap 4 components)

                  class Settings extends Component {
                      constructor(props) {
                          super(props);
              
                          this.state = {
                            popoversOpen: [] // array open popovers
                          }
                      }
              
                      // toggle my popovers
                      togglePopoverHelp = (selected) => (e) => {
                          const index = this.state.popoversOpen.indexOf(selected);
                          if (index < 0) {
                            this.state.popoversOpen.push(selected);
                          } else {
                            this.state.popoversOpen.splice(index, 1);
                          }
                          this.setState({ popoversOpen: [...this.state.popoversOpen] });
                      }
              
                      render() {
                          <div id="settings">
                              <button id="PopoverTimer" onClick={this.togglePopoverHelp(1)} className="btn btn-outline-danger" type="button">?</button>
                              <Popover placement="left" isOpen={this.state.popoversOpen.includes(1)} target="PopoverTimer" toggle={this.togglePopoverHelp(1)}>
                                <PopoverHeader>Header popover</PopoverHeader>
                                <PopoverBody>Description popover</PopoverBody>
                              </Popover>
              
                              <button id="popoverRefresh" onClick={this.togglePopoverHelp(2)} className="btn btn-outline-danger" type="button">?</button>
                              <Popover placement="left" isOpen={this.state.popoversOpen.includes(2)} target="popoverRefresh" toggle={this.togglePopoverHelp(2)}>
                                <PopoverHeader>Header popover 2</PopoverHeader>
                                <PopoverBody>Description popover2</PopoverBody>
                              </Popover>
                          </div>
                      }
                  }
              

              【讨论】:

                【解决方案11】:

                onClick 函数必须通过一个返回 handleButtonChange() 方法的函数。否则它将自动运行,并以错误/警告结束。使用以下方法解决问题。

                onClick={() =&gt; this.handleButtonChange(false)}

                【讨论】:

                  猜你喜欢
                  • 2017-12-20
                  • 2017-05-16
                  • 2016-12-13
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2017-02-22
                  • 1970-01-01
                  • 2017-09-04
                  相关资源
                  最近更新 更多