学习 React 19.2 新特性
这篇文章“费曼一下”React 19.2 的一些新特性。
<Activity /> – React 版本的 Keep-Alive
<Activity /> 是一个新的内置组件,帮助我们控制不可见部分(比如 Tabs 组件隐藏起来的部分)的行为。
使用体验类似于 Vue 的 v-show,我们可以用它来隐藏不需要渲染的组件。
被隐藏的组件:
- 通过调用 cleanup 来清理 effect(比如事件绑定、持续存在的 timer 等),以减少不必要的性能损耗
- 保持低优先级活跃,在浏览器空闲的时候静默更新 UI
- 当切换至可见时,渲染成本更低,性能更好(比条件渲染(如 condition &&)或 display: none 性能好)
这意味着在 Tab 页之间切换时感受更丝滑——不再需要重新获取数据,也不会再丢失滚动位置。
相比于条件渲染:
{activeTab === 'reports' && <Reports />}
{activeTab === 'settings' && <Settings />}
当切换 Tab 的时候:
- React 会卸载旧组件,再挂载新的组件
- 旧组件的状态会丢失
- effect 会重新执行
而使用 Activity,不再需要频繁的卸载挂载,也不需要手动重新设置状态,effect 会暂停。
而相比于 display: none,同样是隐藏组件,但后台 effect 变得更方便可控。
另一个使用场景是预渲染。如果条件渲染的话,只有条件为 true 时组件内部逻辑才会启动。而 Activity,即使隐藏起来也可以默默在后台获取数据,减少用户等待时间(当然这个特性比较 meta,在 useEffect 里 fetch 数据肯定不行)。
我的评价是:早就该出了。Keep-Alive 都多少年了,React 才想起来搞一个。
useEffectEvent – Effect 依赖数组的大救星
这东西在设计 useEffect 的时候不就应该考虑到吗?怎么才放出来。
举一个最简单的例子,假设 WebSocket 成功连接之后打印用户名字。
你就不得不把 user.name 放到依赖数组里。
useEffect(() => {
const connection = createConnection();
connection.on('message', (msg) => {
console.log('New message for', user.name);
});
return() => connection.disconnect();
}, [user.name]);
这就意味着 user.name 改变(或者别的什么与 WebSocket 无关的,比如官方文档例子是页面主题 Theme),就会导致 WebSocket 的重连、页面抖动。
现在可以通过 useEffectEvent 来保持 effect 稳定了。
const onMessage = useEffectEvent((msg) => {
console.log('New message for', user.name);
});
useEffect(() => {
const connection = createConnection();
connection.on('message', onMessage);
return() => connection.disconnect();
}, []);
现在:
- effect 只运行一次
- onMessage 总能拿到最新的 user.name
- 减少了 ESLint 的报错
cacheSignal() — 渐渐变成了 Next 的模样
这个特性专为服务器组件设计。
简单来说就是告诉用户 cache() 的生命周期何时结束,这样用户可以取消掉已经没必要的操作,比如过久的读取数据。
之前我们可以通过 cache 来缓存或者说记忆化 fetch 结果。
import { cache } from'react';
const fetchPostMeta = cache(async (postId) => {
console.log(`Fetching metadata for post ${postId}`);
const res = awaitfetch(`https://api.example.com/posts/${postId}/meta`);
return res.json();
});
asyncfunctionPost({ postId }) {
const meta = awaitfetchPostMeta(postId);
return (
<div>
<h1>Post {postId}</h1>
<p>Views: {meta.views}</p>
</div>
);
}
如果用户在 fetch 完成之前离开页面,API 请求仍然在后台运行。
这会导致服务器资源浪费。现在可以通过传给 fetch 一个 signal,让它在 cache 失效时自动取消无效的请求。
import { cache, cacheSignal } from'react';
const fetchPostMeta = cache(async (postId, { signal }) => {
console.log(`Fetching metadata for post ${postId}`);
const res = awaitfetch(`https://api.example.com/posts/${postId}/meta`, { signal });
return res.json();
});
asyncfunctionPost({ postId }) {
const signal = cacheSignal();
const meta = awaitfetchPostMeta(postId, { signal });
return (
<div>
<h1>Post {postId}</h1>
<p>Views: {meta.views}</p>
</div>
);
}
其他
还有一些特性也特别 Meta,比如 Partial Pre-rendering、批量处理 Suspense 等。
显然这些特性不是给普通用户准备的,更多的是为 Next 这类框架提供支持。
Partial Pre-rendering(PPR):简单来说,现在页面静态部分(比如 header/footer)可以被预渲染和缓存了,动态部分可以等晚些再 resume。
const result = awaitprerender(<Page />);
const html = renderToString(result.prelude);
React 会先渲染静态部分(result.prelude),把这部分发送给浏览器,再等到动态数据获取完毕后把动态部分 resume。这样用户会立即看到内容,React 会在后续无缝地填充其余的东西。
Batching Suspense Boundaries:同样为 SSR 准备。
此前,Suspense 部分会在不同时间渲染,导致页面抖动。现在它会等待一小段时间将多个 Suspense 组合在一起,让变更效果更自然。