use()
是 React19 提升异步开发体验最重要的 hook。也是让 useEffect 重要性大幅度削弱的主要原因。
我们可以利用 use 读取 Promise
中的值。
1const value = use(promise)2// 读取到的 value 值是 promise 中 resolve 出来的值
也可以使用 use 读取 context 中的资源,后续详细介绍该能力
这里我们需要特别注意的是,Promise 是指的一个已经创建好的 Promise 对象,并且,在该 promise 对象中已经有了确定的 resolve
的结果,use 读取的是 resolve 的值。
注意观察一下下面两种写法
第一种是已经有了结果状态的 Promise 对象
1const _api2 = new Promise((resolve) => {2resolve({ value: '_api2' })3})45// good6const result = use(_api2)
第二种是函数运行创建 Promise 对象,此时我们需要注意,虽然 _api3 执行之后会立即返回一个带有 resolve 结果状态的 Promise,但是 use 并不能第一时间读取到其值。
1const _api3 = () => {2return new Promise(resolve => {3resolve({ value: '_api3' })4})5}67// bad: get an error8const result = use(_api3())
如果我们直接使用第二种,那么运行之后,React19 会给你如下一个报错。
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.
一个完整的案例代码以及演示效果如下
10import { use } from 'react'20import { createRandomMessage } from 'utils/index'30import Message from './message'4050const __api = new Promise<{ value: string }>((resolve) => {60resolve({ value: createRandomMessage() })70})8090export default function Demo01() {10const result = use(__api)11return (12<Message message={result.value} />13)14}
这是一个反模式,并不建议在实践中真的这样使用
和其他 hook 一样,use()
必须在函数组件中使用。但是很不一样的是,use 可以在循环和条件判断语句中使用。
1if (!loading) {2result = use(_api2)3}
完整的代码与最终的演示效果如下,你可以在演示案例中多次点击切换按钮查看交互效果。
10import { use, useState } from 'react'20import { createRandomMessage } from 'utils/index'30import Skeleton from 'components/ui/skeleton'40import Message from './message'5060const __api = new Promise<{ value: string }>((resolve) => {70resolve({ value: createRandomMessage() })80})9010export default function Demo02() {11const [loading, setLoading] = useState(false)12let result = { value: '' }13if (!loading) {14result = use(__api)15}1617return (18<>19{loading ? <Skeleton /> : <Message message={result.value} />}20<div className='mt-4 text-right'>21<button className='button' onClick={() => setLoading(!loading)}>切换</button>22</div>23</>24)25}26
通常,我们在处理异步请求时,也会结合 promise 来使用。但是我们并不能直接使用 use 来读取异步请求中的 promise,因为我们已经非常明确,use 只能读取有确定 resolve 结果的 promise 中的值。但是有可能第一时间异步请求包装的 promise 状态为 pending。因此在这种情况下,我们必须结合 Suspense 来使用。
当然,为了加强对 use 的理解,我们也准备了一个不顾任何风险提示,强行等 promise 请求完成之后使用 use 去读取它的值的案例。代码与演示效果如下。
10import React, { useState, use, useRef } from 'react';20import Skeleton from 'components/ui/skeleton'30import Message from './message'40import { getMessage } from './api'5060export default function Demo03() {70let [loading, setLoading] = useState(true)8090const promise = useRef(getMessage().then(res => {10setLoading(false)11return res12}))1314let result = { value: '' }1516if (!loading) {17result = use(promise.current)18return <Message message={result.value} />19}2021return <Skeleton />22}
点击预览工具栏中的刷新按钮可以重新加载执行该组件。
我们可以观察一下效果,在目前的 React 版本中,并不能合理的处理好这种存在风险的读取方式,虽然我们最终读取到了 promise 中的值,内容也顺利渲染出来了,但是中间存在一次明显的闪烁。这种体验非常糟糕。
因此,在实践中,如果我们要读取异步请求的 promise 中的值,必须结合 Suspense 来处理。
接下来我们要学习 Suspense 的详细知识。