【问题标题】:Why the binding event is executed after the setstate is re-rendered为什么在 setstate 重新渲染后执行绑定事件
【发布时间】:2019-09-01 21:51:35
【问题描述】:

Test组件状态为num,组件实现点击按钮num+1的功能,按钮绑定自增方法,keyup也绑定方法,方法使用setState重新渲染num的值,但是鼠标点击按钮的效果和键盘触发事件不一样。请告诉我为什么?

当我点击按钮时,控制台首先记录num,然后是done
但是当我按下回车键时,控制台会记录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>
        )
    }
}

【问题讨论】:

  • 你在其他地方有 Button 组件吗?还是用大写 B 的 Button 拼写错误?
  • @duc mai 我从第三方库中导入Button,只需要一次,别无他处

标签: javascript reactjs


【解决方案1】:

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

React 批处理在事件处理程序和生命周期方法中发生的状态更新......需要明确的是,这仅适用于 React 控制的合成事件处理程序

我不想引用整个答案,请阅读它,但在你的情况下&lt;Button onClick={this.add}... 一个“反应控制的合成事件处理程序”document.body.addEventListener('keyup', this.add); 添加一个事件监听器不是 React 基础架构的一部分。所以 Button onClick setState 调用是批处理的,直到渲染完成(并且在批处理执行之前不会调用您的回调,其中 keyup setState 调用不会批处理并立即发生 - 在console.log(num) 语句之前)。

我认为在你的情况下这不会有任何后果,但我认为这表明你非常关注你注意到的细节。 种情况它变得重要,但我认为你不会在这个组件中遇到它们。我希望这会有所帮助!

【讨论】:

  • 感谢您的帮助,有道理!顺便问一下,你能举一些它变得重要的例子吗?
  • @Lancy React 类组件中的默认状态机制——setState({count: count+1},...)——被设计成一种不会经常出现的方式。您几乎可以通过一次调用 setState 在函数中进行所需的任何状态更新,并传入所有新的状态值。我使用新的 Hooks API 在代码中遇到了这个问题。基本函数 setState 示例显示了类似 const [count, setCount] = useState(initialCount) 的内容,并且往往以许多 setXX 函数结尾。在非批处理函数(任何异步)中,您将获得每个函数的渲染,setCountsetMax 等。
  • 最近打算升级16.8版本的react,尝试使用Hooks,希望有更好的编码体验。非常感谢。
【解决方案2】:

将对象用作第一个参数的setState 方法将按照here 的描述异步执行该方法。因此,您的代码中控制台日志的顺序可能每次都不同。

您看到单击事件和键盘事件之间存在差异的原因是单击事件是React.SyntheticEvent,而键盘事件是 DOM 事件。处理点击事件似乎需要更少的时间,因此您的 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 之后的代码始终等到状态更新。

【讨论】:

  • @Ids van der Zee React.SyntheticEvent 和您解释的 DOM 事件似乎很有意义,但我想知道进一步的原因。 Async 和 await 实际上解决了我的问题。谢谢!
猜你喜欢
  • 2019-12-20
  • 2023-04-01
  • 1970-01-01
  • 2019-01-24
  • 2020-11-09
  • 1970-01-01
  • 1970-01-01
  • 2020-08-08
  • 1970-01-01
相关资源
最近更新 更多