[英]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'));
}
我希望它能以下列方式工作:
waiting
class 到正文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.