我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

react项目中,大家都知道state的变化,一般会引起界面的更新。一些界面的更新会比较耗时,并且相对而言,这些耗时界面的更新也并不是那么重要。那么这种情况下,就可以用到useDeferredValue来延迟对应状态值的更新,进而改善界面的更新效果。这个也是属于react项目优化的部分,不使用useDeferredValue的话,也没有啥大问题,就是可能会有些界面卡顿而已。

苏南大叔:react教程,如何使用useDeferredValue延迟state更新界面? - useDeferredValue延迟state更新界面
react教程,如何使用useDeferredValue延迟state更新界面?(图3-1)

苏南大叔的程序如此灵动博客,记录苏南大叔和计算机代码的故事。测试环境:create-react-app@5.0.1react@18.2.0react-dom@18.2.0node@16.14.2

方案一【useDeferredValue】

本文的场景下,有两个菜单,每个菜单下面有“5000”个子菜单。【个数这么多的目的,就是要造出渲染的延迟效果】。场景模拟的就是点击切换菜单的效果。

import React from 'react';
const menuConfig = {
  苏南: Array(5000).fill('1').map((item, index) => "menu-" + item + "-" + index),
  大叔: Array(5000).fill('2').map((item, index) => "menu-" + item + "-" + index),
}
function DeferValueDemo() {
  const [activeMenu, setActiveMenu] = React.useState('苏南');
  const handleChangeTab = (activeMenuItem) => {
    setActiveMenu(activeMenuItem)                                  // 立即更新
  }
  const activeMenuDefer = React.useDeferredValue(activeMenu);      // 状态延时更新
  const renderData = menuConfig[activeMenuDefer];                  // 使用滞后状态
  // const renderData = menuConfig[activeMenu];                    // 不使用滞后状态
  return <div>
    <div className='tab' >
      {Object.keys(menuConfig).map((item, index) =>
        <span key={index} className={(activeMenu === item) ? 'activeMenu' : ""} onClick={() => handleChangeTab(item)}>{item}</span>
      )}
    </div>
    <ul className='content'>
      { renderData.map(item => <li key={item}>{item}</li>) }
    </ul>
  </div>
}
export default function App() {
  return (
    <>
      <style>{`span.activeMenu{color:red;}`}</style>
      <DeferValueDemo />
    </>
  )
}

苏南大叔:react教程,如何使用useDeferredValue延迟state更新界面? - useDeferredValue-code
react教程,如何使用useDeferredValue延迟state更新界面?(图3-2)

在本文中,为了准确甄别出useDeferredValue的效果,可以观察下面两者发生的先后顺序:父菜单的颜色变化、子菜单的内容变化。

情况一:使用延迟状态版本

核心代码如下:

const [activeMenu, setActiveMenu] = React.useState('苏南');
const activeMenuDefer = React.useDeferredValue(activeMenu);
  • activeMenu就是平常概念上的state值,它的变化会立刻引发其他相关界面的变化。
  • activeMenuDefer就是activeMenu的延迟版本,它的变化所引起的界面变化的优先级最低。

在使用延迟状态的情况下,效果是父菜单先变红色,然后延迟1秒左右子菜单内容才变化(主要是子菜单太多了)。

情况二:不使用延迟状态版本

如果不想使用延迟的状态版本,那么可以这样写:

const renderData = menuConfig[activeMenu];

在不使用延迟状态的情况下,效果是:延迟1秒左右子菜单内容才变化,然后父菜单先红色,就是说界面被卡死了。

方案二【useTransition】

实际上也可以使用useTransition()来解决本文的需求,代码如下:

import React from 'react';
const menuConfig = {
  苏南: Array(5000).fill('1').map((item, index) => "menu-" + item + "-" + index),
  大叔: Array(5000).fill('2').map((item, index) => "menu-" + item + "-" + index),
}
function DeferValueDemo() {
  const [activeMenu, setActiveMenu] = React.useState('苏南');
  const [renderData, setRenderData] = React.useState(menuConfig[activeMenu])
  const [isPending, startTransition] = React.useTransition();
  const handleChangeTab = (activeMenu) => {
    setActiveMenu(activeMenu)                // 立即更新
    startTransition(() => {                  // 延迟更新
      setRenderData(menuConfig[activeMenu])
    })
  }
  return <div>
    <div className='tab' >
      {Object.keys(menuConfig).map((item, index) =>
        <span key={index} className={(activeMenu === item) ? 'activeMenu' : ""} onClick={() => handleChangeTab(item)}>{item}</span>
      )}
    </div>
    <ul className='content'>
      {isPending && <div> loading... </div>}
      {renderData.map(item => <li key={item}>{item}</li>)}
    </ul>
  </div>
}
export default function App() {
  return (
    <>
      <style>{`span.activeMenu{color:red;}`}</style>
      <DeferValueDemo />
    </>
  )
}

苏南大叔:react教程,如何使用useDeferredValue延迟state更新界面? - useTransition解决方案
react教程,如何使用useDeferredValue延迟state更新界面?(图3-3)

方案对比

两种方案最终的效果,都是差不多的。但是,原理上来说,还是有所区别的。

名称区别
useDeferredValue()延迟的是状态值
useTransition()延迟的是状态组装界面的过程

参考文字:

结束语

更多react相关经验文字,请点击:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   react