[英]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.