useTransition
更新时间:2023-02-22 15:57:53标签:reactweb前端
示例一
代码
1import { FC, memo, ReactNode, useRef, useState, useTransition } from 'react';2import { Button } from 'antd';34interface SlowItemProps {5 children?: ReactNode;6}7const SlowItem: FC<SlowItemProps> = (props) => {89 const { children } = props;10 const now = useRef(Date.now());1112 while (Date.now() - now.current < 10) {13 // 强行阻塞代码10毫秒14 }1516 return (17 <span>{ children }</span>18 );19};2021const SlowList = memo(() => {22 return (23 <>24 {25 // 循环200次就是阻塞2秒钟26 new Array(200).fill(1).map((v, k) => {27 return (28 <SlowItem key={k}>{ k }</SlowItem>29 );30 })31 }32 </>33 );34});35SlowList.displayName = 'SlowList';3637const UseTransitionList: FC = () => {3839 const [show, setShow] = useState(false);40 const [isPending, startTransition] = useTransition();4142 return (43 <div>44 <div>45 <Button onClick={() => setShow(true)}>展示列表(同步渲染)</Button>46 <span>同步的按钮点击后,rerender过程同步执行,所以浏览器会卡死5秒钟</span>47 </div>48 <div>49 <Button50 onClick={() => {51 startTransition(() => setShow(true));52 }}53 >展示列表(startTransition)</Button>54 <span>{ `${isPending}` }</span>55 <Button onClick={() => setShow(false)}>隐藏列表</Button>56 <span>使用startTransition,rerender过程异步执行,可以随时中断rerender过程</span>57 </div>58 {59 show ?60 <SlowList /> :61 null62 }63 </div>64 );65};6667export { UseTransitionList };
效果
同步的按钮点击后,rerender过程同步执行,所以浏览器会卡死5秒钟
false使用startTransition,rerender过程异步执行,可以随时中断rerender过程
示例二(与Suspense配合食用)
代码
1import { FC, lazy, Suspense, useState, useTransition } from 'react';2import { sleep } from '@shared/utils';3import { Button, Space } from 'antd';45// 将lazy组件再延迟2000毫秒以便更好的模拟网络延迟6const LazyExample = lazy(() => import('./lazy-example').then(res => sleep(2000).then(() => res)));78const SuspenseExample: FC = () => {910 const [show, setShow] = useState(false);11 const [isPending, startTransition] = useTransition();1213 return (14 <>15 <div>16 <Space>17 <Button onClick={() => setShow(true)}>显示</Button>18 <Button onClick={() => setShow(false)}>隐藏</Button>19 <Button20 onClick={() => {21 startTransition(() => setShow(true));22 }}23 >显示(startTransition)</Button>24 <span>{`${isPending}`}</span>25 </Space>26 </div>27 <hr />28 <Suspense fallback={<span>loading...</span>}>29 {30 show ?31 <LazyExample /> :32 <span>初始状态</span>33 }34 </Suspense>35 </>36 );37};3839export { SuspenseExample };
效果
false
初始状态
可以看到,使用startTransition时,Suspense的fallback没有生效,在异步组件加载完成之前,页面UI始终没有变化
应用场景
懒加载路由进度条
切换路由时注意浏览器上方会有进度条显示
代码(模拟异步加载的路由场景)
1import { ComponentType, FC, lazy, LazyExoticComponent, Suspense, useCallback, useState, useTransition } from 'react';2import { Button } from 'antd';3import { sleep, NProgress } from '@shared/utils';45const AsyncLoad = <T extends ComponentType>(factory: () => Promise<{default: T}>) => {6 const C = lazy(() => {7 const load = factory().then(res => sleep(2000).then(() => res));8 NProgress.start();9 load.finally(() => {10 NProgress.done();11 });12 return load;13 }) as LazyExoticComponent<any>;14 return (15 <C />16 );17};1819const Router1 = AsyncLoad(() => import('./router-1'));20const Router2 = AsyncLoad(() => import('./router-2'));21const Router3 = AsyncLoad(() => import('./router-3'));2223const Routes: FC<{ pathname: string }> = (props) => {24 if (props.pathname === '/page-1') {25 return Router1;26 }27 if (props.pathname === '/page-2') {28 return Router2;29 }30 if (props.pathname === '/page-3') {31 return Router3;32 }33 return <span>切换路由时注意浏览器上方会有进度条显示</span>;34};3536const LazyProgress: FC = () => {37 const [pathname, setPathname] = useState('/');3839 const [, startTransition] = useTransition();40 const navigate = useCallback((url: string) => {41 startTransition(() => {42 setPathname(url);43 });44 }, []);4546 return (47 <div>48 <div>49 <Button onClick={() => navigate('/page-1')} type={pathname === '/page-1' ? 'primary' : 'text'}>页面1</Button>50 <Button onClick={() => navigate('/page-2')} type={pathname === '/page-2' ? 'primary' : 'text'}>页面2</Button>51 <Button onClick={() => navigate('/page-3')} type={pathname === '/page-3' ? 'primary' : 'text'}>页面3</Button>52 </div>53 <hr/>54 <Suspense fallback={<span>loading...</span>}>55 <Routes pathname={pathname} />56 </Suspense>57 </div>58 );5960};6162export { LazyProgress };