最近 React 发布了 18 版,具有一些很棒的功能。
在这篇文章中,我们将仔细研究与性能相关的特性。
使用变换
它是并发概念的一部分,您可以在其中确定状态更新的优先级。
紧急状态更新可以优先于不太紧急(阻塞)的更新。
如何使用它以及这个新钩子如何提高您的应用程序性能将在示例中学习,该示例可以在这里找到。
这是我们的例子。这是一个简单的,我们有一个打开模式的按钮,在模式中,我们呈现了一个包含 500 条评论的列表。
500 条评论很多,但这在大多数设备上都可以正常工作。
import { useState , useTransition } from " react " ; import Comments from " ../components/Comments " ; import Modal from " ../components/Modal " ; import data from " ../data/index.json " ; export default function Home () { const [ isOpen , setIsOpen ] = useState ( false ); const [ comments , setComments ] = useState ([]); return ( < div className = " p-4 " > < button className = " px-4 py-2 border-none rounded-sm bg-blue-800 text-white " onClick = {() => { setIsOpen ( true ); setComments ( data ); }} > Toggle modal < /button > < Modal isOpen = { isOpen } onClose = {() => { setIsOpen ( false ); setComments ([]); }} > < Comments comments = { comments } / > < /Modal > < /div > ); }
但是,如果我们减慢Comment
组件的渲染速度? 事情会变得更有趣。
为了实现这一点,我添加for
循环以迭代一百万次。
const Comment = ({ name , email , body , className , onClick }: CommentProps ) => { const soooSloww = []; for ( let i = 0 ; i < 1000000 ; i ++ ) { soooSloww . push ( i ); } return ( < article className = { className } onClick = { onClick } > < h3 className = " font-semibold " > { name } < /h3 > < h4 className = " text-gray-500 italic " > { email } < /h4 > < p > { body } < /p > < /article > ); };
现在,当您单击按钮打开模式时,几秒钟内没有任何反应。
这是因为浏览器忙于渲染缓慢的 500 Comment
组件。
一段时间后,呈现模态和评论。
从用户的角度来看,这是非常糟糕的用户体验。
如何改进它?
我们可以优先考虑渲染,在我们的示例中,更重要的是先渲染模态框,然后再渲染评论。
useTransition
钩子返回两个变量, pending
是一个布尔标志,表示转换尚未完成,以及startTransition
函数,您可以在其中执行不太重要的状态更新。
const [ pending , startTransition ] = useTransition ();
现在,我们的示例看起来像这样
export default function Home () { const [ isOpen , setIsOpen ] = useState ( false ); const [ comments , setComments ] = useState ([]); const [ pending , startTransition ] = useTransition (); return ( < div className = " p-4 " > < button className = " px-4 py-2 border-none rounded-sm bg-blue-800 text-white " onClick = {() => { setIsOpen ( true ); startTransition (() => { setComments ( data ); }); }} > Toggle modal < /button > < Modal isOpen = { isOpen } onClose = {() => { setIsOpen ( false ); setComments ([]); }} > { pending ? " Loading... " : < Comments comments = { comments } /> } < /Modal > < /div > ); }
您可以注意到,在按钮单击时,我们更新状态以显示模式,这是具有较高优先级的操作,并更新startTransition
函数中的注释状态,该函数告诉 React 状态更新具有较低优先级。
此外,我们使用pending
标志向用户显示“正在加载…”文本,同时呈现缓慢的评论。
现在,单击按钮后,您将立即获得如下所示的模式:
更好的用户体验! ?
使用延迟值
这个钩子还告诉 React 某些状态更新具有较低的优先级。
它类似于useTransition
,老实说,我不确定当你应该更喜欢useDeferredValue
而不是useTransition
时,什么是用例,如果你有任何想法,请在评论中告诉我。 ?
我们之前的示例现在看起来像这样并且行为类似,除了我们没有pending
标志。
export default function UseDeferredValues () { const [ isOpen , setIsOpen ] = useState ( false ); const [ comments , setComments ] = useState ([]); const commentsToRender = useDeferredValue ( comments ); return ( < div className = " p-4 " > < button className = " px-4 py-2 border-none rounded-sm bg-blue-800 text-white " onClick = {() => { setIsOpen ( true ); setComments ( data ); }} > Toggle modal < /button > < Modal isOpen = { isOpen } onClose = {() => { setIsOpen ( false ); setComments ([]); }} > < Comments comments = { commentsToRender } / > < /Modal > < /div > ); }
自动配料
当你使用 React 时,你的目标应该是尽可能少地重新渲染。
现在 React 18 通过自动批处理帮助您实现这一目标。
早期版本的 React 仅在onClick
或onChange
等 React 事件处理程序中批量处理多个状态更新,以避免多次重新渲染并提高性能。
现在,React 在 React 事件处理程序、promise、setTimeout、本机事件处理程序等中批量状态更新。
const AutomaticBatching = () => { const [ countOne , setCountOne ] = useState ( 0 ); const [ countTwo , setCountTwo ] = useState ( 0 ); console . log ( " render " ); const onClick = useCallback (() => { setCountOne ( countOne + 1 ); setCountTwo ( countTwo + 1 ); }, [ countOne , countTwo ]); useEffect (() => { document . getElementById ( " native-event " ). addEventListener ( " click " , onClick ); return () => document . getElementById ( " native-event " ) . removeEventListener ( " click " , onClick ); }, [ onClick ]); const onClickAsync = () => { fetch ( " https://jsonplaceholder.typicode.com/todos/1 " ). then (() => { setCountOne ( countOne + 1 ); setCountTwo ( countTwo + 1 ); }); }; const onClickTimeout = () => setTimeout (() => { setCountOne ( countOne + 1 ); setCountTwo ( countTwo + 1 ); }, 200 ); return ( < div className = " p-4 " > < ul className = " mb-8 " > < li > Count one : { countOne } < /li > < li > Count two : { countTwo } < /li > < /ul > < Button onClick = { onClick } > Batching in click event < /Button > < Button id = " native-event " className = " ml-4 " > Batching in native click event < /Button > < Button className = " ml-4 " onClick = { onClickAsync } > Batching in fetch < /Button > < Button className = " ml-4 " onClick = { onClickTimeout } > Batching in timeout < /Button > < /div > ); };
在此示例中,您可以看到在每个事件处理程序中,我们有两个状态更改,但只有一个 rerender 。您可以注意到,每个事件都有一个 console.log。
改进的悬念
Suspense
以这种方式与React.lazy
一起使用,它会暂停组件渲染,直到它被加载并在此期间渲染一个回退。
const LazyComponent = lazy (() => import ( " ../components/LazyComponent " )); < Suspense fallback = { < div > Loading ... < /div>} > < LazyComponent /> < /Suspense >
这是提高性能的好方法,您不会在初始捆绑包中包含您不需要立即使用的应用程序的某些部分(例如模式)。
但是, Suspense
并不是一个新功能,它存在于 React 的早期版本中,新的是现在它可以与以前不同的服务器端渲染一起使用。
这就是大家,希望你喜欢新版本的 React。 ?
上面的所有示例都可以在这里找到。
原文: https://dev.to/markoarsenal/react-18-performance-improvements-26el