一、問題案例
function App() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(2);
fn(); // 點擊后打印 0
};
const fn = () => {
console.log(count);
};
return (
<div className="App">
<button onClick={handleClick}>點擊</button>
</div>
);
}
二、原因分析
1.為什么在 fn 中打印出來的 count 是 0 呢?
因為 React 合成事件中,為了減少 render 次數,提高性能,React 會將多次狀態更新收集起來,最后一次更新,所以在 React 合成事件中,狀態更新是異步的,fn 和 setCount 在同一個宏任務中,這時候 React 還沒有 render,所以獲取到的 count 還是上一次閉包里的值 0。
三、解決思路
1.通過傳參的形式,將最新的值,傳遞給 fn
2.render 之后,在 useEffect 中獲取
3.自定義一個 hook useSyncCallback
四、useSyncCallback 代碼實現
import { useEffect, useState, useCallback } from 'react'
const useSyncCallback = callback => {
const [proxyState, setProxyState] = useState({ current: false })
const [params, setParams] = useState([])
const Func = useCallback((...args) => {
setParams(args)
setProxyState({ current: true })
}, [proxyState])
useEffect(() => {
if (proxyState.current === true) setProxyState({ current: false })
}, [proxyState])
useEffect(() => {
proxyState.current && callback(...params)
})
return Func
}
export default useSyncCallback
使用 useSyncCallback
function App() {
const [count, setCount] = useState(0);
const handleClick = () => {
setstate(2);
fn(); // 打印 2
};
/** 將fn的方法傳遞給 useSyncCallback 然后返回一個新的函數 */
const fn = useSyncCallback(() => {
console.log(count);
});
return (
<div className="App">
<button onClick={handleClick}>點擊</button>
</div>
);
}
export default App;