React18新hook:useDeferredValue

useDeferredValue 的基础用法

useDeferredValue 可以让你延迟更新 UI 的某些部分

用法如下:

jsx 复制代码
const deferredValue = useDeferredValue(someValue);

其中someValue是你想要延迟的值,它可以是任何类型。

deferredValue的渲染有两种情况:

  1. 在初始渲染时deferredValue的值将与someValue的值相同。
  2. 在UI更新期间 ,因为deferredValue的优先级较低,即使并发模式下deferredValue已在后台更新,React也会先使用旧值渲染,当其它高优先级的状态更新完成,才会把deferredValue新值渲染出来。

useDeferredValue 应用示例

在新内容加载期间显示旧内容

比如,在搜索时,我们可能会配合 Suspense 组件,当发起搜索请求时,展示 fallback 的内容,请求完毕时,再显示最新的结果。

jsx 复制代码
import { Suspense, useState } from 'react';
import SearchResults from './SearchResults.js';

export default function App() {
  const [query, setQuery] = useState('');
  return (
    <>
      <label>
        Search albums:
        <input value={query} onChange={e => setQuery(e.target.value)} />
      </label>
      <Suspense fallback={<h2>Loading...</h2>}>
        <SearchResults query={query} />
      </Suspense>
    </>
  );
}

像这样,有一个 loading 效果:

然而,我们可以通过 useDeferredValue,在搜索时,延迟更新的结果也就是在发送搜索请求时,不展示 loading,旧数据依然展示(下面的例子我们给旧数据置灰),当请求完毕时,再去渲染最新的数据

jsx 复制代码
import { Suspense, useState, useDeferredValue } from 'react';
import SearchResults from './SearchResults.js';

export default function App() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);
  return (
    <>
      <label>
        Search albums:
        <input value={query} onChange={e => setQuery(e.target.value)} />
      </label>
      <div style={{
          opacity: isStale ? 0.5 : 1,
          transition: isStale ? 'opacity 0.2s 0.2s linear' : 'opacity 0s 0s linear'
        }}>
          <SearchResults query={deferredQuery} />
      </div>
    </>
  );
}
  • 当搜索 a 时:
  • 然后再搜索 ab 时,旧数据置灰,当请求成功时,再渲染最新的数据

延迟渲染 UI 的某些部分

你还可以将 useDeferredValue 作为性能优化的手段。当你的 UI 某个部分重新渲染很慢、没有简单的优化方法,同时你又希望避免它阻塞其他 UI 的渲染时,使用 useDeferredValue 很有帮助。

想象一下,你有一个文本框和一个组件(例如图表或长列表),在每次按键时都会重新渲染:

jsx 复制代码
// app.jsx
import { useState, useDeferredValue } from 'react';
import SlowList from './SlowList.js';

export default function App() {
  const [text, setText] = useState('');
  return (
    <>
      <input value={text} onChange={e => setText(e.target.value)} />
      <SlowList text={text} />
    </>
  );
}

每次你输入的时候,SlowList 都会接受到新的 props,然后重新渲染,就会导致卡顿,然而,我们可以通过 useDeferredValue 去做延迟渲染

jsx 复制代码
// app.jsx
import { useState, useDeferredValue } from 'react';
import SlowList from './SlowList.js';

export default function App() {
  const [text, setText] = useState('');
  const deferredText = useDeferredValue(text);
  return (
    <>
      <input value={text} onChange={e => setText(e.target.value)} />
      <SlowList text={deferredText} />
    </>
  );
}

// SlowList.jsx
import { memo } from 'react';

const SlowList = memo(function SlowList({ text }) {
  // 仅打印一次。实际的减速是在 SlowItem 组件内部。
  console.log('[ARTIFICIALLY SLOW] Rendering 250 <SlowItem />');

  let items = [];
  for (let i = 0; i < 250; i++) {
    items.push(<SlowItem key={i} text={text} />);
  }
  return (
    <ul className="items">
      {items}
    </ul>
  );
});

function SlowItem({ text }) {
  let startTime = performance.now();
  while (performance.now() - startTime < 1) {
    // 每个 item 暂停 1ms,模拟极其缓慢的代码
  }

  return (
    <li className="item">
      Text: {text}
    </li>
  )
}

export default SlowList;

效果如下:

  • 在输入停止前,不会 rerender

  • 输入停止后,再渲染

useDeferredValue 和 节流、防抖的区别

从上面的学习可以看出来,useDeferredValue在某些场景下是可以替换掉节流防抖

防抖与节流的局限性

  • 这两种方法都是为了控制函数的执行频率,但它们是阻塞的,可能会导致不流畅的用户体验。

useDeferredValue的优势

  • 它与React深度集成,可以适应用户的设备。如果设备性能好,延迟的重新渲染会很快完成;如果设备性能差,重新渲染会相应地延迟。
  • 它不需要选择固定的延迟,与防抖和节流不同。
  • useDeferredValue执行的重新渲染是可中断的。这意味着在React重新渲染期间,如果发生了其他更新,React会中断当前的渲染并处理新的更新。

适用场景

  • 如果要优化的工作不是在渲染期间进行的,例如减少网络请求,那么防抖和节流仍然是有用的。
  • 如果优化的目标是和渲染有关的,建议使用useDeferredValue
相关推荐
阿伟来咯~2 小时前
记录学习react的一些内容
javascript·学习·react.js
吕彬-前端2 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱2 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
bysking3 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
September_ning7 小时前
React.lazy() 懒加载
前端·react.js·前端框架
web行路人7 小时前
React中类组件和函数组件的理解和区别
前端·javascript·react.js·前端框架
番茄小酱0017 小时前
Expo|ReactNative 中实现扫描二维码功能
javascript·react native·react.js
Rattenking9 小时前
React 源码学习01 ---- React.Children.map 的实现与应用
javascript·学习·react.js
熊的猫11 小时前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
小牛itbull15 小时前
ReactPress:重塑内容管理的未来
react.js·github·reactpress