本篇依然来自于我们的 《前端周刊》 项目!
由团队成员 嘿嘿 翻译,他的文章风格稳健而清晰,注重结构与逻辑的严谨性,善于用简洁的语言将复杂技术拆解成易于理解的知识点~
欢迎大家 进群 与他探讨 React 最新趋势,并持续追踪全球前端领域的最新动态!

引言
多年来,React 开发者习惯使用 useContext
在全局共享状态,而不用把 props 一层层传递下去。虽然这种方式可行,但即将到来的 use()
API 带来了更强大的能力,远远超越了单纯读取 context 的功能。

通过 use()
,你不仅能做到 useContext
可以做到的一切,还能直接在组件中无缝处理异步数据,比如 API 调用或 Promise。这意味着更同意的写法、更干净的代码,以及一种全新的状态管理和数据获取思路。
在本文结束时,你将清楚理解 use()
API 如何改善现代 React 中的状态管理和数据获取方式。
前置条件
在继续之前,你需要准备:
- 已在本地安装 Node.js v24
- React v19
项目初始化
我们用 Vite 来初始化一个 React 项目。 打开终端并运行:
perl
npm create vite@latest my-project
接着进入项目目录:
bash
cd my-project
然后启动开发服务器:
arduino
npm run dev
本文示例会使用 Tailwind CSS 来做样式。
在 use() 之前
在 React 的新 use()
API 出现之前,若要从 API 获取数据,你需要导入并使用三个 Hook,例如:
scss
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
这种写法显得冗长:你需要同时管理三个状态变量(data
、loading
、error
),并结合 useEffect
来触发请求,例如:
javascript
import React, { useState, useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error('Failed to fetch data');
}
const result = await response.json();
setData(result);
} catch (e) {
setError(e);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
if (loading) {
return <div>加载中...</div>;
}
if (error) {
return <div>错误: {error.message}</div>;
}
return (
<div>
{/* 在此渲染数据 */}
</div>
);
}
这个组件通过 useEffect
拉取 API 数据,并用 useState
管理状态。它初始化了三个状态:data
存放结果,loading
表示加载状态,error
捕获请求错误。当组件挂载时,会触发异步函数请求数据,成功则存入 data
,失败则存入 error
。
使用 use()
接下来看 use()
的场景。使用 use
,你不再需要手动管理这三个状态,也不需要 useEffect
来触发请求。
use()
可以直接在组件中读取 Promise 的值,无需 useEffect
与 useState
来单独管理 loading 和 error。与普通 Hook 不同,它可以在循环和条件语句中运行,但依然必须在组件或 Hook 内部调用。
结合 Suspense
,它可以以最小的配置实现丝滑的加载体验。语法如下:
ini
const value = use(resource);
构建 Demo 应用
我们来实际体验一下 use
的威力。
设置 API 调用 在 src
文件夹中创建 api.js
,粘贴如下代码:
javascript
let usersPromise;
async function fetchUsers() {
const response = await fetch('<https://jsonplaceholder.typicode.com/users>');
if (!response.ok) {
throw new Error('Failed to fetch users');
}
return response.json();
}
export function getUsersPromise() {
if (!usersPromise) {
usersPromise = fetchUsers();
}
return usersPromise;
}
上面的 API 调用中,我们通过 getUsersPromise
来缓存(memoize)Promise,这样即便组件重新渲染,数据也只会请求一次。接着我们用 use
来解包这个 Promise。
使用 use 解包 Promise
创建 UserList.jsx
并添加如下导入:
javascript
import React, { use } from 'react';
import { getUsersPromise } from './api';
然后写组件:
javascript
function UserList() {
const users = use(getUsersPromise());
return (
<div>
<h1 className='text-center py-5 text-2xl font-bold'>用户列表</h1>
<ul className='grid grid-cols-5 gap-5'>
{users.map((user) => (
<li key={user.id} className=' bg-gray-500 py-10 text-center rounded-[5px] text-gray-200'>
<strong>{user.name}</strong> - {user.email}
</li>
))}
</ul>
</div>
);
}
export default UserList;
这里我们把 getUsersPromise
传给了 use
,这样它会直接解包返回的 Promise,并赋值给 users
。接着我们遍历并渲染数据。
你注意到没有?这里完全不需要 useState
和 useEffect
! 得益于 use
,我们可以声明式地处理数据请求、渲染,以及加载与错误处理。接下来我们还会结合 Suspense
和 ErrorBoundary
来进一步优化用户体验。
错误边界(ErrorBoundary)
use
API 借助 Suspense
和 ErrorBoundary
来管理不同状态。由于 React 没有内置 ErrorBoundary
,我们需要自己实现。 在项目中新建 ErrorBoundary.jsx
并粘贴以下代码:
javascript
import React from "react";
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error: error };
}
componentDidCatch(error, errorInfo) {
console.error("未捕获错误:", error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
<div className="p-10 text-center text-red-700 font-bold border-2 border-solid border-red-800">
<h2>出错了。</h2>
<p>{this.state.error && this.state.error.message}</p>
</div>
);
}
return this.props.children;
}
}
export default ErrorBoundary;
该组件能捕获子组件树中任意错误,使用了两个生命周期方法:
static getDerivedStateFromError(error)
componentDidCatch(error, errorInfo)
第一个在抛出错误后触发,第二个在状态更新后触发。
显示组件
修改 App.jsx
内容如下:
javascript
import React, { Suspense } from "react";
import UserList from "./UserList";
import ErrorBoundary from "./ErrorBoundary";
const FallbackLoader = () => (
<div className="p-10 text-center font-bold">正在加载用户...</div>
);
function App() {
return (
<div className="App bg bg-gray-200 h-screen w-screen flex flex-col items-center justify-center px-5">
<main>
<ErrorBoundary>
<Suspense fallback={<FallbackLoader />}>
<UserList />
</Suspense>
</ErrorBoundary>
</main>
</div>
);
}
export default App;
FallbackLoader
组件提供加载时的占位 UI。 在 App
中,我们用 Suspense
包裹 <UserList/>
,并传入 FallbackLoader
作为 fallback,再外层加上 ErrorBoundary
处理错误。
运行 npm run dev
即可看到效果。

接下来,我们给应用加上 暗黑模式。
use vs useContext
为了展示 use
API 如何超越 useContext
,我们来实现暗黑模式。 在 use
出现之前,如果要实现暗黑模式,你大概率会用 useContext
,例如:
javascript
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const toggleTheme = () => setTheme(theme === 'light' ? 'dark' : 'light');
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
然后通过 useContext
访问:
ini
const ThemeToggle = () => {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button onClick={toggleTheme}>
切换到 {theme === 'light' ? '深色' : '浅色'} 模式
</button>
);
};
而使用 use
,写法更简洁。
使用 use 实现暗黑模式
创建 ThemeContext.jsx
并写入:
javascript
import React, { createContext, useState, useEffect } from "react";
const ThemeContext = createContext(null);
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState(() => {
const savedTheme = localStorage.getItem("theme");
return savedTheme || "light";
});
useEffect(() => {
document.body.setAttribute("data-theme", theme);
localStorage.setItem("theme", theme);
}, [theme]);
const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === "light" ? "dark" : "light"));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
export default ThemeContext;
然后在 UserList.jsx
中引入:
javascript
import ThemeContext from './contexts/ThemeContext';
并通过 use
来消费 context:
php
const { theme } = use(ThemeContext);
更新后的 UserList.jsx
:
javascript
import React, { use } from 'react';
import { getUsersPromise } from './api';
import ThemeContext from './contexts/ThemeContext';
function UserList() {
const { theme } = use(ThemeContext);
const users = use(getUsersPromise());
return (
<div>
<h1 className='text-center py-5 text-2xl font-bold text-gray-900 dark:text-gray-100 transition-colors duration-300'>
用户列表
</h1>
<ul className='grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-5'>
{users.map((user) => (
<li
key={user.id}
className='py-10 text-center rounded-[5px] shadow-md bg-gray-500 text-gray-200 dark:bg-gray-700 dark:text-gray-100 transition-colors duration-300 ease-in-out'
>
<strong>{user.name}</strong> - {user.email}
</li>
))}
</ul>
</div>
);
}
export default UserList;
接着在 App.jsx
中引入并实现 ThemeToggle
,完整内容这里省略。
运行后,你将看到用户列表支持明暗模式切换。
use 的局限性
虽然 use
很强大,但仍有一些限制:
- Promise 必须稳定 :传递给
use
的 Promise 必须被缓存,否则会导致无限循环。
例如把 getUsersPromise
改成直接返回 fetchUsers()
:
javascript
export function getUsersPromise() {
return fetchUsers();
}
运行后你会发现网络请求无限触发。 这是因为每次渲染都会生成新的 Promise,从而再次触发 Suspense
,导致组件不断重新挂起。
因此,推荐始终搭配 Suspense + ErrorBoundary 来使用,以保证良好的用户体验。
总结
一句话总结: use()
API 是 React 的一次重大飞跃,尤其在服务端优先(server-first)应用中,它能替代很多 useContext
的场景,并带来更强的异步友好能力。
在本教程中,我们体验了 use()
的应用:解包 Promise、结合 Suspense 与错误边界,以及无需样板代码即可实现主题切换。
有了 use()
,状态管理和数据获取更简洁、更直观,也让现代 React 应用的开发变得更高效、顺滑。