Skip to content

use

use() 是 React19 提升异步开发体验最重要的 hook。也是让 useEffect 重要性大幅度削弱的主要原因。

我们可以利用 use 读取 Promise 中的值。也可以使用 use 读取 context 中的资源

正确理解 promise

tsx
const value = use(promise);

这里我们需要特别注意的是,Promise 是指的一个已经创建好的 Promise 对象,并且,在该 promise 对象中已经有了确定的 resolve 的结果,use 读取的是 resolve 的值。

注意观察一下下面两种写法

第一种是已经有了结果状态的 Promise 对象

tsx
const _api2 = new Promise((resolve) => {
  resolve({ value: '_api2' });
});

// good
const result = use(_api2);

第二种是函数运行创建 Promise 对象,此时我们需要注意,虽然 _api3 执行之后会立即返回一个带有 resolve 结果状态的 Promise,但是 use 并不能第一时间读取到其值。

tsx
function _api3() {
  return new Promise((resolve) => {
    resolve({ value: '_api3' });
  });
}

// bad: get an error
const result = use(_api3());

如果我们按照以往的习惯,直接使用第二种,那么运行之后,React19 会给你如下一个报错。

::: error

async/await is not yet supported in Client Components, only Server Components. This error is often caused by accidentally adding 'use client' to a module that was originally written for the server.

:::

在条件判断中使用

和其他 hook 一样,use() 必须在函数组件中使用。

但是很不一样的是,use 可以在循环和条件判断语句中使用。我们这里如下案例来演示这个结论。

在这个例子中,use 被使用在 if(!loading) 条件判断中来获取 result 的值。

tsx
import { use, useState } from 'react';
import Message from './Message';
import Button from './Button';
import Skeleton from './Skeleton';

const _api2 = new Promise((resolve) => {
  resolve({ value: 'Unlike React Hooks, use can be called within loops and conditional statements like if. Like React Hooks, the function that calls use must be a Component or Hook.' });
});

export default function Demo01() {
  const [loading, setLoading] = useState(false);
  let result = { value: '' };
  if (!loading) {
    result = use(_api2);
  }

  return (
    <>
      {loading ? <Skeleton /> : <Message message={result.value} />}
      <div className="mt-4 text-right">
        <Button signal onClick={() => setLoading(!loading)}>切换</Button>
      </div>
    </>
  );
}

在异步请求中使用

通常,我们在处理异步请求时,也会结合 promise 来使用。

但是我们并不能直接使用 use 来读取异步请求中的 promise,因为我们已经非常明确,use 只能读取有确定 resolve 结果的 promise 中的值

但是有可能第一时间异步请求包装的 promise 状态为 pending。

因此在这种情况下,我们必须结合 Suspense 来使用

在实践中,如果我们要读取异步请求的 promise 中的值,必须结合 Suspense 来处理 否则,虽然我们最终读取到了 promise 中的值,内容也顺利渲染出来了,但是中间存在一次明显的闪烁,这种体验非常的糟糕。

在低版本中使用

早在 React 16 的后期版本中,就提供了对 Suspense 的支持,

因此,我们只需要在这个基础之上,自己定义一个 use 方法,去读取 promise 中的值就可以实现use + Suspense 结合的效果了。

自定义的代码如下

js
export default function use(promise) {
  if (promise.status === 'fulfilled') {
    return promise.value;
  }
  else if (promise.status === 'rejected') {
    throw promise.reason;
  }
  else if (promise.status === 'pending') {
    throw promise;
  }
  else {
    promise.status = 'pending';
    promise.then((result) => {
      promise.status = 'fulfilled';
      promise.value = result;
    }, (reason) => {
      promise.status = 'rejected';
      promise.reason = reason;
    });
    throw promise;
  }
}

Contributors

作者:Long Mo
字数统计:805 字
阅读时长:3 分钟
Long Mo
文章作者:Long Mo
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Longmo Docs