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.
一个完整的案例代码以及演示效果如下
01import { use } from 'react'02import { random } from 'utils/index'03import Message from './message'0405const __api = new Promise<{ value: string }>((resolve) => {06resolve({ value: random[0] })07})0809export default function Demo01() {10const result = use(__api)11return (12<Message message={result.value} />13)14}
这是一个反模式, 并不建议在实践中真的这样使用
和其他 hook 一样, use() 必须在函数组件中使用. 但是很不一样的是, use 可以在循环和条件判断语句中使用.
1if (!loading) {2result = use(_api2)3}
完整的代码与最终的演示效果如下, 你可以在演示案例中多次点击切换按钮查看交互效果.
01import { use, useState } from 'react'02import { random } from 'utils/index'03import Skeleton from 'components/zmui/skeleton'04import Message from './message'0506const __api = new Promise<{ value: string }>((resolve) => {07resolve({ value: random[0] })08})0910export default function Demo02() {11const [loading, setLoading] = useState(false)12let result = { value: '' }13if (!loading) {14result = use(__api)15}1617return (18<div className='p-4'>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</div>24)25}26
通常, 我们在处理异步请求时, 也会结合 promise 来使用. 但是我们并不能直接使用 use 来读取异步请求中的 promise, 因为我们已经非常明确, use 只能读取有确定 resolve 结果的 promise 中的值. 但是有可能第一时间异步请求包装的 promise 状态为 pending. 因此在这种情况下, 我们必须结合 Suspense 来使用.
当然, 为了加强对 use 的理解, 我们也准备了一个不顾任何风险提示, 强行等 promise 请求完成之后使用 use 去读取它的值的案例. 代码与演示效果如下.
01import React, { useState, use, useRef } from 'react';02import Skeleton from 'components/zmui/skeleton'03import Message from './message'04import { getMessage } from './api'0506export default function Demo03() {07let [loading, setLoading] = useState(true)0809const 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}
点击预览工具栏中的刷新按钮可以重新加载执行该组件.
最终虽然读取到了 promise 中的值, 但是一定要记住, 这种写法是不安全的, 并不建议在实践中真的这样使用. 而要在实践中采用 Suspense 来处理.
接下来我们要学习 Suspense 的详细知识.