为什么在重新渲染setstate之后执行绑定事件

时间:2019-04-11 09:44:45

标签: javascript reactjs

Test组件的状态为num,该组件实现单击num + 1按钮的功能,该按钮绑定到自增量方法,keyup也绑定到该方法,该方法使用setState重新呈现num的值,但是用鼠标单击按钮的效果与键盘触发事件不同。你能告诉我为什么吗?

当我单击按钮时,控制台首先记录num,然后记录done
但是当我按Enter键时,控制台会先记录done,然后记录num

React15.5

class Test extends React.PureComponent {
    constructor(){
        super();
        this.state = {
            num : 1
        }
    }
    add = () => {
        const {num} = this.state;
        this.setState({num:num+1},()=>{
            console.log("done")
        })
        console.log(this.state.num)
    }

    componentDidMount() {
        document.body.addEventListener('keyup', this.add);
    }
    componentWillUnmount() {
        document.body.removeEventListener('keyup', this.add);
    }

    render() {
        return(
            <Button onClick={this.add} >add</Button>
            <span>{this.state.num}</span>
        )
    }
}

2 个答案:

答案 0 :(得分:1)

setState方法与对象作为第一个参数一起使用,将按照here的方式异步执行该方法。因此,每次代码中控制台日志的顺序可能会有所不同。

您看到click事件和键盘事件之间存在差异的原因是,click事件是React.SyntheticEvent,而键盘事件是DOM事件。似乎处理click事件花费的时间更少,因此您的console.log(this.state.num)在React完成更新状态之前执行。

如果您想在两个触发器上看到相同的行为,我建议使用componentDidUpdate生命周期方法。此生命周期方法将等待更新完成。

修改

您可以使add方法异步,然后等待setState方法的执行。产生的添加方法:

add = async () => {
    await this.setState(
      {
        num: state.num + 1
      },
      () => {
        console.log("done");
      }
    );
    console.log(this.state.num);
};

这将确保await this.setState之后的代码始终等待状态更新。

答案 1 :(得分:1)

我认为@ ids-van-der-zee的答案需要考虑一些重要点。但是我认为导致控制台输出差异的根本原因是此答案:https://stackoverflow.com/a/33613918/4114178

  

反应批次状态更新发生在事件处理程序和生命周期方法中...要清楚,这仅在React控制的合成事件处理程序中起作用

我不想引用整个答案,请仔细阅读,但对于您而言,<Button onClick={this.add}... “反应控制的综合事件处理程序”,其中document.body.addEventListener('keyup', this.add);添加一个事件监听器,它不是React基础架构的一部分。因此,Button onClick setState调用将一直进行批处理,直到渲染完成为止(在执行批处理之前,不会调用您的回调,而keyup setState调用不会进行批处理并立即发生在console.log(num)语句之前)。

我认为这对您没有任何影响,但是我认为它非常关注您注意到的细节。在某些情况下它变得很重要,但我认为您不会在本部分中找到它们。我希望这会有所帮助!