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

我们可以利用 use 读取 Promise 中的值。

                                        
1
const value = use (promise)
2
// 读取到的 value 值是 promise 中 resolve 出来的值

也可以使用 use 读取 context 中的资源,后续详细介绍该能力

1 、正确理解 promise

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

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

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

                                        
1
const _api2 = new Promise (( resolve ) => {
2
resolve ({ value: '_api2 ' })
3
})
4
5
// good
6
const result = use (_api2)

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

                                        
1
const _api3 = () => {
2
return new Promise ( resolve => {
3
resolve ({ value: '_api3 ' })
4
})
5
}
6
7
// bad: get an error
8
const 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.

一个完整的案例代码以及演示效果如下

预览
index.tsx
message.tsx
                                            
1
import { use } from 'react '
2
import { createRandomMessage } from 'utils/index '
3
import Message from './message '
4
5
const __api = new Promise <{ value : string }>(( resolve ) => {
6
resolve ({ value: createRandomMessage () })
7
})
8
9
export default function Demo01 () {
10
const result = use (__api)
11
return (
12
< Message message ={ result.value } />
13
)
14
}

2 、在条件判断中使用

这是一个反模式,并不建议在实践中真的这样使用

和其他 hook 一样,use() 必须在函数组件中使用。但是很不一样的是,use 可以在循环和条件判断语句中使用。

                                        
1
if ( ! loading) {
2
result = use (_api2)
3
}

完整的代码与最终的演示效果如下,你可以在演示案例中多次点击切换按钮查看交互效果。

预览
index.tsx
message.tsx
                                            
1
import { use, useState } from 'react '
2
import { createRandomMessage } from 'utils/index '
3
import Skeleton from 'components/ui/skeleton '
4
import Message from './message '
5
6
const __api = new Promise <{ value : string }>(( resolve ) => {
7
resolve ({ value: createRandomMessage () })
8
})
9
10
export default function Demo02 () {
11
const [ loading , setLoading ] = useState ( false )
12
let result = { value: '' }
13
if ( ! loading) {
14
result = use (__api)
15
}
16
17
return (
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

3 、在异步请求中使用

通常,我们在处理异步请求时,也会结合 promise 来使用。但是我们并不能直接使用 use 来读取异步请求中的 promise,因为我们已经非常明确,use 只能读取有确定 resolve 结果的 promise 中的值。但是有可能第一时间异步请求包装的 promise 状态为 pending。因此在这种情况下,我们必须结合 Suspense 来使用。

当然,为了加强对 use 的理解,我们也准备了一个不顾任何风险提示,强行等 promise 请求完成之后使用 use 去读取它的值的案例。代码与演示效果如下。

预览
index.tsx
message.tsx
api.ts
                                            
1
import React, { useState, use, useRef } from 'react ' ;
2
import Skeleton from 'components/ui/skeleton '
3
import Message from './message '
4
import { getMessage } from './api '
5
6
export default function Demo03 () {
7
let [loading, setLoading] = useState ( true )
8
9
const promise = useRef ( getMessage (). then ( res => {
10
setLoading ( false )
11
return res
12
}))
13
14
let result = { value: '' }
15
16
if ( ! loading) {
17
result = use (promise.current)
18
return < Message message ={ result.value } />
19
}
20
21
return < Skeleton />
22
}

点击预览工具栏中的刷新按钮可以重新加载执行该组件。

我们可以观察一下效果,在目前的 React 版本中,并不能合理的处理好这种存在风险的读取方式,虽然我们最终读取到了 promise 中的值,内容也顺利渲染出来了,但是中间存在一次明显的闪烁。这种体验非常糟糕。

因此,在实践中,如果我们要读取异步请求的 promise 中的值,必须结合 Suspense 来处理。

接下来我们要学习 Suspense 的详细知识。

上一篇
3、创建项目
下一篇
5、Suspense
专栏首页
到顶
专栏目录