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))
const Demo = () => {
const [keyword, setKeyWord] = useState('');
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>
</>
);
}