React 18相关更新

111 阅读1分钟

自动批处理(Automatic batching)

在react17之前,同时设置两个state数据,会有两次更新,例如下面这个例子,会执行两次render。

function App() {
    const [text, setText] = useState('1');
    const [count, setCount] = useState(1);
      
    function handleClick() {
         setText(pre => pre + '1');
         setCount(pre => pre + 1);
    }
    
    function fetchData() {
        return new Promise((resolve) => setTimeout(resolve, 1000));
    }
    
    function handleFetchClick() {
        fetchData().then(res => {
            handleClick();
        });
    }
    
    return (
        <button onClick={handleClick}>click</button>
        <button onClick={handleFetchClick}>fetchClick</button>        
        <h1>{count}</h1>
        <div>{text}</div>
        <Logger/>
    );
}


function Logger() {
    useEffect(() => {
        console.log('update');
    })
    
    console.log('render');
    return null;
}

react的17版本有对React在事件处理函数中对多个状态更新进行批处理,比如例子中的handleClick,点击button后只会执行渲染一次,但对于像setTimeoutPromise.then等异步操作上表现会有差异,比如例子里的handleFetchClick

因此react团队在18版本增加了自动批的处理,两次state的更新被合并成一次更新,只会打印一次updaterender

不过react也提供了一个种可以不使用批处理的,就是使用flushSync,如果你再某个场景下需要更新完state后读取DOM,那么你需要先渲染更新一遍,比如

    function handleClick() {
         flushSync(() => {
            setText(pre => pre + '1');
         })
         setCount(pre => pre + 1);
    }

具体想了解的可以看这里

SuSpense

新的hook

useId

获取一个独立的id

const id = useId();
...

useTransation

不阻塞ui的情况下更新状态

const [isPending, startTransation] = useTransation();
const [tab, setTab] = useState();

useEffect(() => {
    startTransation(() => setTab('aa');
})

if (isPending) {return ...}
else return <Tab tab={tab} />