startTransition、useTransition、useDeferredValue

463 阅读2分钟

startTransition、useTransition、useDeferredValue

startTransition

  • 用法:接收一个函数
  • 作用:会将函数内改变的状态优先级降低(双缓冲),绘制完成,直接替换上一个dom
  • 在下面的demo中的表现形式有点像节流函数

检索下拉框demo

  • 输入框内输入内容,会多次重新渲染提示内容,数据量大造成ui交互阻塞(无法输入),体验极差
import React, { useState, useEffect, startTransition, useDeferredValue } from 'react';
interface Props {
    keyword: string
}
const Suggestions = (props: Props) => {
    const [words, setWords] = useState<Array<string>>([])
    useEffect(() => {
        if (!props.keyword) {
            setWords([]);
            return;
        };
        setWords(new Array(10000).fill(0).map((v, i) => i + props.keyword))

    }, [props.keyword])
    return (
        <ul>
            {
                words.map((word: string) => <li key={word}>{word}</li>)
            }
        </ul>
    )
}
const Demo = () => {
    const [keyword, setKeyWord] = useState('');
    return (
        <>
            <div>
                输入框:<input value={keyword} onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                    setKeyWord(e.target.value)
                }} />
            </div>
            <Suggestions keyword={keyword} />

        </>
    )
}
export default Demo;
  • 使用startTransition优化
    • 虽然仍有卡顿,但ui输入阻塞情况明显改善
    • 降低列表渲染优先级
setWords(new Array(10000).fill(0).map((v, i) => i + props.keyword))
改为
startTransition(()=>{
    setWords(new Array(10000).fill(0).map((v, i) => i + props.keyword))
})

useDeferredValue

  • startTransition的语法糖
  • 使用:接收一个入参变量,当入参变量set后,自动使用startTransition包裹set
// 还原
startTransition(()=>{
    setWords(new Array(10000).fill(0).map((v, i) => i + props.keyword))
})
为
setWords(new Array(10000).fill(0).map((v, i) => i + props.keyword))

// 更新demo
const Demo = () => {
    const [keyword, setKeyWord] = useState('');
    // 监听keyword变化,并在变化后使用startTransition包裹
    // 伪代码:startTransition(()=>{setKeyWord(keyword)})
    const defValue = useDeferredValue(keyword);
    return (
        <>
            <div>
                输入框:<input value={keyword} onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
                    setKeyWord(e.target.value)
                }} />
            </div>
            <Suggestions keyword={defValue} />
        </>
    )
}

useTransition

  • 返回元组[isPending, startTransition]
  • 当startTransition执行isPending为true,执行完毕自动变false
  • startTransition等同于react.startTransition
  • 双缓冲,降低优先级
    • 以Suspense的动态请求demo举例,每次更新都会触发fallback渲染,但我们如果不希望他loading,且想自己拿到loading态做一些事情就可以使用这个hook
    • 当函数执行结束,会直接替换上一个dom节点,而不是进入fallback
  • 暴露在外的isPending很灵活,可以用来做按钮置灰等操作
const Demo = () => {
    const [result, setResult] = useState(createUser(fetchUser(1)));
    const [isPending, startTransition] = useTransition();
    return (
        <>
            <Suspense fallback={"加载中"}>
                <User result={result} />
            </Suspense>
            {
                isPending && 'hooks自己维护的loading态'
            }
            <button onClick={() => {
                // 随机切换用户
                startTransition(
                    () => {
                        setResult(createUser(fetchUser(Math.random())))
                    }
                )
            }}>切换用户</button>
        </>

    );
}