欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

使用过渡(useTransition)初探,React 18 新功能优先级初探

最编程 2024-04-27 13:42:16
...

翻译: 卷帘依旧

原文地址: dmitripavlutin.com/react-usetr…

在Web应用中,UI更新(比如在输入框中输入字段,从下拉框选择一个值)的优先级应该高一些,而其他操作(比如显示列表过滤内容)优先级要低一些。

直到现在,React也并未提供提高UI更新优先级的工具。

幸运的事,从React 18(2021.06发布了alpha版本)开始,我们就可以开启并发模式-concurrentconcurrent模式允许将UI更新标记为高优先级的或者可中断的低优先级操作。

在这篇文章中,你会掌握如何使用useTransition()将UI更新标记为低优先级,这种操作对大量的非紧急更新非常有用。

1. useTransition()钩子

默认情况下,我们认为React中的所有更新都是紧急的(也就是所有更新的优先级相同)。那会导致一个问题-快速更新会被大量更新拖慢速度。

image.png

然而,从React 18中新增特性-concurrency之后,你就可以将某些更新标记为可中断的非紧急的-也就是所谓的transitions。这种新特性在大量的UI更新操作中尤其有效,比如过滤一个较大的列表。

image.png

useTransition()这个钩子函数使得用户能够在React组件中使用concurrent模式特性

调用const [isPending, startTransition] = useTransitionHook()返回一个具有两个成员的数组:

  • isPending: 指明这个transition正在加载中(pending)
  • startTransition(回调): 允许用户将回调中的任何UI更新标记为transitions.
import { useTransition } from 'react';

function MyComponent() {

    const [isPending, startTransition] = useTransition();

    // ...

    const someEventHandler = (event) => {

        startTransition(() => {

            // Mark updates as transitions

            setValue(event.target.value);
        });
    }

    return <HeavyComponent value={value} />;

}

为了能够使用useTransition()钩子,请确保开启concurrent模式

2. 紧急的大量UI更新

接下来考虑一个所有更新都很紧急的例子,以及这些大量更新是如何影响用户体验的。

比如有一个员工名字列表和一个查找员工的姓名搜索框,这个组件会高亮显示匹配搜索内容的员工姓名。

以下是可能实现的代码:

import { useState } from 'react';
export function FilterList({ names }) {
  const [query, setQuery] = useState('');
  const changeHandler = ({ target: { value } }) => setQuery(value);

  return (
    <div>
      <input onChange={changeHandler} value={query} type="text" />
      {names.map((name, i) => (
        <ListItem key={i} name={name} highlight={query} />
      ))}
    </div>
  );
}
function ListItem({ name, highlight }) {
  const index = name.toLowerCase().indexOf(highlight.toLowerCase());
  if (index === -1) {
    return <div>{name}</div>;
  }
  return (
    <div>
      {name.slice(0, index)}
      <span className="highlight">
        {name.slice(index, index + highlight.length)}
      </span>
      {name.slice(index + highlight.length)}
    </div>
  );
}

在线运行示例

<FilterList names={names} />接受一个大的姓名列表。在组件内部,query是包含查询字符串的状态变量。输入框是一个控制组件-用于在用户输入改变时更新query状态变量。

打开示例页面并快速在输入框内键入查询字段,你可能会注意到键入延迟以及用户界面在明显的时间内没有响应。

为什么会出现这种现象,如何解决呢?

在用户键入时更新输入框的值是一个必须快速执行的紧急任务,更新高亮显示匹配列表是一个繁重且不紧急的任务。

大量的非紧急任务落后于轻量的紧急任务。

useTransition()钩子能够帮助你区分紧急的UI更新和非紧急的UI更新。

3. 大量的UI更新作为过渡(transitions)

之前已经提高了,你可以使用useTransition()告诉React哪些UI更新是紧急的(比如更新输入框的值)和哪些UI更新是不紧急的transitions(比如更新高亮匹配查询内容的姓名列表)

我们来对FilterList组件做一下必要的调整。

首先,我们调用[isPending, startTransition] = useTransition()钩子来访问startTransition()函数,然后专门这个transition创建了一个保存状态的状态变量。

import React, { useState, useTransition } from "react";

export function FilterList({ names }) {
  const [query, setQuery] = useState("");
  const [highlight, setHighlight] = useState("");

  const [isPending, startTransition] = useTransition();

  const changeHandler = ({ target: { value } }) => {
    setQuery(value);
    startTransition(() => setHighlight(value));
  };

  return (
    <div>
      <input onChange={changeHandler} value={query} type="text" />
      {isPending ? 'pending' : (
        names.map((name, i) => (
          <ListItem key={i} name={name} highlight={highlight} />
        ))
      )}
    </div>
  );
}

function ListItem({ name, highlight }) {
  const index = name.toLowerCase().indexOf(highlight.toLowerCase());
  if (index === -1) {
    return <div>{name}</div>;
  }
  return (
    <div>
      {name.slice(0, index)}
      <span className="highlight">
        {name.slice(index, index + highlight.length)}
      </span>
      {name.slice(index + highlight.length)}
    </div>
  );
}

在线运行transition示例

打开这个使用了transitionos特性的示例,如果你非常快速地在输入框中键入,你会注意到高亮列表的延迟更新。

React将紧急任务(当用户键入时更新输入框)的更新和非紧急任务(高亮显示过滤内容)的渲染区分开了,这样的操作提升了用户体验。

4. 总结

React中的并发模式(concurrent mode)将紧急任务和非紧急任务区分开,使UI更新更加人性化。

在开启React 18并发模式新特性之后,你可以使用useTransition()钩子进而使用startTransition(callback)函数。

useTransition()使你能够将默写更新标记为过渡(transitions):

const [isPending, startTransition] = useTransition();
startTransition(() => {
  // Mark updates as transitions
  setStateValue(newValue);
});

P.S.: 除了使用useTranstion(),使用React 18的另一个新特性-userDeferredValue()是不是能够除掉<FilterList>组件中重复的highlight状态变量呢?在评论区写下你的想法吧????