use()
是 React19 提升异步开发体验最重要的 hook。也是让 useEffect 重要性大幅度削弱的主要原因。
我们可以利用 use 读取 Promise
中的值。
1
const
value
=
use
(promise)
2
// 读取到的 value 值是 promise 中 resolve 出来的值
也可以使用 use 读取 context 中的资源,后续详细介绍该能力
这里我们需要特别注意的是,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.
一个完整的案例代码以及演示效果如下
10
import
{ use }
from
'react '
20
import
{ createRandomMessage }
from
'utils/index '
30
import
Message
from
'./message '
40
50
const
__api
= new
Promise
<{
value
:
string
}>((
resolve
)
=>
{
60
resolve
({ value:
createRandomMessage
() })
70
})
80
90
export default function
Demo01
()
{
10
const
result
=
use
(__api)
11
return
(
12
<
Message
message
={
result.value
}
/>
13
)
14
}
这是一个反模式,并不建议在实践中真的这样使用
和其他 hook 一样,use()
必须在函数组件中使用。但是很不一样的是,use 可以在循环和条件判断语句中使用。
1
if
(
!
loading) {
2
result
=
use
(_api2)
3
}
完整的代码与最终的演示效果如下,你可以在演示案例中多次点击切换按钮查看交互效果。
10
import
{ use, useState }
from
'react '
20
import
{ createRandomMessage }
from
'utils/index '
30
import
Skeleton
from
'components/ui/skeleton '
40
import
Message
from
'./message '
50
60
const
__api
= new
Promise
<{
value
:
string
}>((
resolve
)
=>
{
70
resolve
({ value:
createRandomMessage
() })
80
})
90
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
通常,我们在处理异步请求时,也会结合 promise 来使用。但是我们并不能直接使用 use 来读取异步请求中的 promise,因为我们已经非常明确,use 只能读取有确定 resolve 结果的 promise 中的值。但是有可能第一时间异步请求包装的 promise 状态为 pending。因此在这种情况下,我们必须结合 Suspense 来使用。
当然,为了加强对 use 的理解,我们也准备了一个不顾任何风险提示,强行等 promise 请求完成之后使用 use 去读取它的值的案例。代码与演示效果如下。
10
import
React, { useState, use, useRef }
from
'react '
;
20
import
Skeleton
from
'components/ui/skeleton '
30
import
Message
from
'./message '
40
import
{ getMessage }
from
'./api '
50
60
export default function
Demo03
()
{
70
let
[loading, setLoading]
=
useState
(
true
)
80
90
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 的详细知识。