繁体   English   中英

更改document.body,修改React state 再次更改body

[英]Change document.body, modify React state and change body again

我想在用户单击按钮时更改主体的 CSS class,并在handleClick中定义的操作完成时将其反转。

我有以下handleClick function:

handleClick = data => {
    document.body.classList.add('waiting');
    this.setState({data}, () => document.body.classList.remove('waiting'));
}

我希望它能以下列方式工作:

  1. 添加waiting class 到正文
  2. 更改组件 state
  3. 从正文中删除waiting class

但它不是那样工作的。 为了让它工作,我必须将handleClick function 更改为如下所示:

handleClick = data => {
    document.body.classList.add('waiting');
    setTimeout(() => this.setState({data}, () => document.body.classList.remove('waiting')))
}

我想知道为什么会这样。 我知道setTimeout正在“延迟”执行this.setState但据我所知this.setState本身是异步的所以它不应该自己延迟吗? 这是否意味着更改document.body.classList也是异步的并且它的执行比this.setState的执行延迟更多?

但它不是那样工作的。

它确实如此,但这并不意味着您在浏览器 window 中看到任何变化。:-)您所做的正是您所描述的:在 state 更改之前设置 class,进行 state 更改,并在state 零钱。

 const {useState} = React; class Example extends React.Component { state = { counter: 0, }; handleClick = () => { console.log("Adding class"); document.body.classList.add('waiting'); // NOTE: Normally this should use the callback form of a state setter, // rather than assuming that `this.state.counter` is up-to-date. But // I wanted to keep using the same type of call as you were console.log("Setting state"); this.setState({counter: this.state.counter + 1}, () => { console.log("Removing class"); document.body.classList.remove('waiting'); }); }; componentDidUpdate() { console.log("componentDidUpdate"); } render() { console.log(`render, counter = ${this.state.counter}`); return <div> <div>{this.state.counter}</div> <input type="button" value="Click Me" onClick={this.handleClick} /> </div>; } }; ReactDOM.render(<Example />, document.getElementById("root"));
 <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

但是状态更改后的回调可能会在 DOM 更新之前发生,并且可能会在页面显示更新以说明body类的更改之前单独发生。

...但据我所知this.setState本身是异步的所以它不应该自己延迟...

state 更新是异步的,是的,但可以非常主动地完成(它甚至可以是一个微任务,在当前任务完成后立即运行,这是在浏览器更新 class 更改的页面显示之前——或者不是,它是一个React 的实现细节)。

这是否意味着更改document.body.classList也是异步的并且它的执行比this.setState的执行延迟更多?

不,变化是同步的,但是直到下一次浏览器绘制显示时你才会看到变化,直到当前任务和它所有排队的微任务完成(或者甚至在那之后;浏览器同步到显示设备的刷新率,因此通常为 60 次/秒 [大约每 16.67 毫秒一次],但对于更高端的显示器和显示适配器可能会更高)。

您的setTimeout(/*...*/, 0)技巧是处理此问题的一种常用方法,尽管当该计时器触发时浏览器可能尚未更新显示(因为再次更新之间通常约为 16.67 毫秒)。 您可以使用requestAnimationFrame挂钩浏览器的页面显示更新周期,它会在浏览器即将更新显示之前为您提供回调。 但与(比如)20 毫秒的延迟相比,这可能有点矫枉过正。

处理此问题的另一种方法是立即添加 class 并使用componentDidUpdate安排在下一个 animation 帧删除它。 但同样,这可能过于复杂。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM