We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
在 react 18 版本中,在 createRoot 模式下,useState、setState 无论是在同步方法中调用,还是在异步方法中调用,setState 都是异步的,原因是 react 18 的 createRoot 下有 auto batching(自动批处理)机制。即在同一次渲染时,合并更新 state。
createRoot
auto batching
reactwg/react-18#21
// APP 组件 function App() { const [count, setCount] = useState(0); const [flag, setFlag] = useState(false); function handleClick() { fetchSomething().then(() => { // react 18 自动批处理 setCount(c => c + 1); setFlag(f => !f); // 只会 re-render 一次 }); } return ( <div> <button onClick={handleClick}>Next</button> <h1 style={{ color: flag ? "blue" : "black" }}>{count}</h1> </div> ); } // 表现相同 function handleClick() { // 方式 1 setCount((c) => c + 1); setFlag((f) => !f); // 只会 re-render 一次 // 同方式 2 setTimeout(() => { setCount((c) => c + 1); setFlag((f) => !f); // 只会 re-render 一次 }, 1000); // 同方式 3 fetch(/*...*/).then(() => { setCount((c) => c + 1); setFlag((f) => !f); // 只会 re-render 一次 }); } // 方式 4 elm.addEventListener("click", () => { setCount((c) => c + 1); setFlag((f) => !f); // 只会 re-render 一次 }); // 主入口 import ReactDOM from 'react-dom/client'; import App from './App.jsx'; // createRoot ReactDOM.createRoot(document.getElementById('root')).render(<App />);
可以使用 flushSync 进行同步更新。注意,这种方式如非必要,并不推荐使用。因为过多的同步更新会带来性能的损耗。
flushSync
import { flushSync } from "react-dom"; const handleClick6 = () => { Promise.resolve().then(() => { console.log("handleClick6 0 :", count); flushSync(() => { console.log(1); setCount((count) => count + 1); console.log("-> 1", count); }); console.log("handleClick6 1 :", count); flushSync(() => { console.log(2); setCount((count) => count + 1); console.log("-> 2", count); }); console.log("handleClick6 2 :", count); }); }; <div onClick={handleClick6}>flushSync {count} </div>
在 react 16.8/17 版本中,setState 同步异步需要区分看
先说结论:
// App.jsx import { useState, useEffect } from "react"; function App() { const [count, setCount] = useState(0); // 合成同步 const handleClick = () => { console.log("---- 执行合成同步事件1 start ----") console.log("handleClick 0 :", count); setCount(count + 1); console.log("handleClick 1 :", count); setCount(count + 1); console.log("handleClick 2 :", count); console.log("---- 执行合成同步事件1 end ----") }; // 合成同步 const handleClick2 = () => { console.log("---- 执行合成同步事件 2 start ----") console.log("handleClick2 0 :", count); setCount((count) => count + 1); console.log("handleClick2 1 :", count); setCount((count) => count + 1); console.log("handleClick2 2 :", count); console.log("---- 执行合成同步事件 2 end ----") }; // 合成异步 const handleClick3 = () => { console.log("---- 执行合成同步事件 3 start ----") setTimeout(() => { console.log("---- 执行合成同步事件 3 定时器内 ----") console.log("handleClick3 0 :", count); setCount(count + 1); console.log("handleClick3 1 :", count); setCount(count + 1); console.log("handleClick3 2 :", count); console.log("---- 执行合成同步事件 3 end ----") }, 1000); }; console.log("render"); useEffect(() => { document.getElementById("div").addEventListener("click", () => { console.log("---- 执行原生同步事件 1 start ----") console.log("原生1 0 :", count); setCount(count + 1); console.log("原生1 1 :", count); setCount(count + 1); console.log("原生1 2 :", count); console.log("---- 执行原生同步事件 1 end ----") }); document.getElementById("div2").addEventListener("click", () => { console.log("---- 执行原生同步事件 2 start ----") console.log("原生2 0 :", count); setCount((count) => count + 1); console.log("原生2 1 :", count); setCount((count) => count + 1); console.log("原生2 2 :", count); console.log("---- 执行原生同步事件 2 end ----") }); document.getElementById("div3").addEventListener("click", () => { console.log("---- 执行原生异步事件 3 start ----") setTimeout(() => { console.log("---- 执行原生异步事件 3 定时器内 ----") console.log("原生3 0 :", count); setCount((count) => count + 1); console.log("原生3 1 :", count); setCount((count) => count + 1); console.log("原生3 2 :", count); console.log("---- 执行原生异步事件 3 end ----") }); }); }, []); return ( <> <div>--- 合成事件 ---</div> <div onClick={handleClick}>合成同步事件 1 {count} </div> <div onClick={handleClick2}>合成同步事件 2 {count} </div> <div onClick={handleClick3}>合成异步事件 3 {count} </div> <hr/> <div>--- 原生事件 ---</div> <div id="div">原生同步事件 1 {count}</div> <div id="div2">原生同步事件 2 {count}</div> <div id="div3">原生异步事件 3 {count}</div> </> ); } export default App; // 主入口 import ReactDOM from 'react-dom'; import App from './App.jsx'; ReactDOM.render(<App />, document.getElementById('root')); // package.json "dependencies": { "react": "^17.0.2", "react-dom": "^17.0.2" }
1.合成事件 - 同步
2.合成事件 - 异步
3.原生事件 - 同步
4.原生事件 - 异步
The text was updated successfully, but these errors were encountered:
No branches or pull requests
React useState、setState 同步异步问题
react 18
在 react 18 版本中,在
createRoot
模式下,useState、setState 无论是在同步方法中调用,还是在异步方法中调用,setState 都是异步的,原因是 react 18 的createRoot
下有auto batching
(自动批处理)机制。即在同一次渲染时,合并更新 state。react 18 如何同步更新
可以使用
flushSync
进行同步更新。注意,这种方式如非必要,并不推荐使用。因为过多的同步更新会带来性能的损耗。在 react 16.8/17 版本中,setState 同步异步需要区分看
react 16.8/17
先说结论:
1.合成事件 - 同步
2.合成事件 - 异步
3.原生事件 - 同步
4.原生事件 - 异步
The text was updated successfully, but these errors were encountered: