React 18 新特性
React 18 概述
React 18 是 React 的最新版本,引入了并发特性(Concurrent Features),提升了用户体验和开发体验。
核心特性
1. 并发渲染(Concurrent Rendering)
概念:
- React 可以中断、暂停和恢复渲染工作
- 根据优先级调度更新
- 提升应用响应性
优势:
- 更好的用户体验
- 避免阻塞主线程
- 更流畅的交互
使用:
import { createRoot } from 'react-dom/client';
// React 18 新的 API
const root = createRoot(document.getElementById('root'));
root.render(<App />);
// React 17 及之前(已废弃)
// ReactDOM.render(<App />, document.getElementById('root'));
2. 自动批处理(Automatic Batching)
概念:
- React 18 自动批处理所有状态更新
- 减少重新渲染次数
- 提升性能
React 17 行为:
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// React 17 会触发两次重新渲染
}
React 18 行为:
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// React 18 自动批处理,只触发一次重新渲染
}
// 异步操作也会批处理
function handleClick() {
fetch('/api').then(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React 18 也会批处理
});
}
强制同步更新:
import { flushSync } from 'react-dom';
function handleClick() {
flushSync(() => {
setCount(c => c + 1);
});
// 这里 count 已经更新
flushSync(() => {
setFlag(f => !f);
});
// 这里 flag 已经更新
}
3. Suspense 增强
服务端渲染支持:
import { Suspense } from 'react';
import { createRoot } from 'react-dom/client';
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<ProfilePage />
</Suspense>
);
}
并发 Suspense:
function App() {
return (
<>
<Suspense fallback={<div>Loading profile...</div>}>
<ProfilePage />
</Suspense>
<Suspense fallback={<div>Loading posts...</div>}>
<PostsPage />
</Suspense>
</>
);
}
Suspense 列表:
import { SuspenseList } from 'react';
function App() {
return (
<SuspenseList revealOrder="forwards" tail="collapsed">
<Suspense fallback={<div>Loading...</div>}>
<ProfilePicture id={1} />
</Suspense>
<Suspense fallback={<div>Loading...</div>}>
<ProfilePicture id={2} />
</Suspense>
<Suspense fallback={<div>Loading...</div>}>
<ProfilePicture id={3} />
</Suspense>
</SuspenseList>
);
}
4. Transitions(useTransition)
概念:
- 标记非紧急更新
- 允许 React 中断这些更新
- 优先处理用户交互
使用:
import { useTransition } from 'react';
function SearchResults() {
const [isPending, startTransition] = useTransition();
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
function handleChange(e) {
const value = e.target.value;
setQuery(value); // 紧急更新
startTransition(() => {
// 非紧急更新
setResults(search(value));
});
}
return (
<div>
{isPending && <div>Searching...</div>}
<input value={query} onChange={handleChange} />
<ResultsList results={results} />
</div>
);
}
优势:
- 保持 UI 响应性
- 避免阻塞用户交互
- 更好的用户体验
5. Deferred Values(useDeferredValue)
概念:
- 延迟更新值
- 允许 React 先处理紧急更新
- 类似防抖但更智能
使用:
import { useDeferredValue } from 'react';
function SearchResults({ query }) {
const deferredQuery = useDeferredValue(query);
const results = useMemo(() => {
return search(deferredQuery);
}, [deferredQuery]);
return <ResultsList results={results} />;
}
与 useTransition 的区别:
useTransition:标记更新为过渡useDeferredValue:延迟值更新
6. useId
概念:
- 生成唯一 ID
- 服务端和客户端一致
- 解决 hydration 不匹配问题
使用:
import { useId } from 'react';
function Checkbox() {
const id = useId();
return (
<>
<label htmlFor={id}>Checkbox</label>
<input id={id} type="checkbox" />
</>
);
}
// 多个 ID
function Form() {
const nameId = useId();
const emailId = useId();
return (
<>
<label htmlFor={nameId}>Name</label>
<input id={nameId} type="text" />
<label htmlFor={emailId}>Email</label>
<input id={emailId} type="email" />
</>
);
}
注意:
- 不要用于列表的 key
- 不要用于生成 CSS 选择器
- 只用于需要唯一 ID 的场景
7. useSyncExternalStore
概念:
- 订阅外部数据源
- 避免 tearing(撕裂)问题
- 适合状态管理库
使用:
import { useSyncExternalStore } from 'react';
function useStore(selector) {
const store = useStoreInstance();
return useSyncExternalStore(
store.subscribe,
() => selector(store.getState()),
() => selector(store.getServerState()) // SSR
);
}
适用场景:
- Redux、Zustand 等状态管理库
- 订阅外部数据源
- 避免并发渲染问题
8. useInsertionEffect
概念:
- 在 DOM 更新前执行
- 主要用于 CSS-in-JS 库
- 避免样式闪烁
使用:
import { useInsertionEffect } from 'react';
function useCSS(rule) {
useInsertionEffect(() => {
const style = document.createElement('style');
style.textContent = rule;
document.head.appendChild(style);
return () => {
document.head.removeChild(style);
};
});
return rule;
}
执行时机:
- 在 DOM 更新前
- 在所有 useLayoutEffect 之前
- 适合注入样式
9. Strict Mode 更新
双重渲染:
- 开发模式下组件会渲染两次
- 帮助发现副作用问题
- 生产环境不受影响
行为:
function Component() {
console.log('render'); // 开发模式会打印两次
useEffect(() => {
console.log('effect'); // 会执行两次
return () => {
console.log('cleanup'); // 也会执行
};
}, []);
return <div>Component</div>;
}
目的:
- 检测副作用
- 确保组件可重用
- 提升代码质量
新的 API
createRoot
替换 ReactDOM.render:
// React 18
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
// 卸载
root.unmount();
// React 17(已废弃)
// ReactDOM.render(<App />, document.getElementById('root'));
hydrateRoot
服务端渲染:
import { hydrateRoot } from 'react-dom/client';
const root = hydrateRoot(document.getElementById('root'), <App />);
迁移指南
1. 更新 React 版本
npm install react@18 react-dom@18
2. 更新入口文件
// 旧代码
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));
// 新代码
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
3. 处理类型定义
npm install @types/react@18 @types/react-dom@18
4. 更新测试库
npm install @testing-library/react@latest
兼容性
浏览器支持
- Chrome、Firefox、Safari、Edge(最新版本)
- 不支持 IE11
库兼容性
已兼容:
- React Router v6
- Redux Toolkit
- React Query
- 大部分主流库
需要更新:
- 部分旧的状态管理库
- 部分测试库
性能提升
1. 更快的渲染
- 并发渲染提升响应性
- 自动批处理减少渲染次数
- 更好的调度算法
2. 更好的用户体验
- 保持 UI 响应性
- 避免阻塞交互
- 更流畅的动画
3. 服务端渲染改进
- Suspense 支持 SSR
- 流式渲染
- 选择性 hydration
最佳实践
1. 使用新的 API
- 使用
createRoot替代ReactDOM.render - 使用
useId生成唯一 ID - 使用
useTransition标记非紧急更新
2. 利用并发特性
- 使用
startTransition包装非紧急更新 - 使用
useDeferredValue延迟值更新 - 使用 Suspense 处理异步加载
3. 注意副作用
- 注意 Strict Mode 的双重渲染
- 确保副作用可重用
- 正确清理资源
总结
React 18 的主要改进:
- 并发渲染:提升应用响应性
- 自动批处理:减少重新渲染
- Suspense 增强:更好的异步处理
- Transitions:优先处理用户交互
- 新 Hooks:useId、useTransition、useDeferredValue 等
- 更好的 SSR:流式渲染和选择性 hydration
建议:
- 尽快升级到 React 18
- 利用并发特性提升用户体验
- 注意兼容性问题
- 遵循最佳实践