React 18于2022年3月29日正式发布,这是React历史上最重要的版本迭代之一。React 18引入了Concurrent Mode并发模式,带来了自动批处理、过渡更新、流式服务端渲染等革命性新特性,同时也为React的未来发展奠定了坚实基础。
本文将全面解析React 18的所有新特性和重大变化,并提供详细的代码示例,帮助你深入理解这个版本的核心改进。
一、Concurrent Mode并发模式 什么是并发模式 Concurrent Mode(并发模式)是React 18最核心的底层架构更新。官方称之为”你永远不需要关心”的功能,因为它是一个底层机制,上层应用开发者通常感知不到它的存在。
并发模式的核心能力是:让React能够同时准备多个版本的UI 。
在传统渲染模式中,React执行状态更新时,整个渲染流程是串行的。一旦开始渲染,就会一直执行到完成,期间无法中断。这就像看电影时,门铃响了必须等电影播放完才能去开门。
而在并发模式下,React会判断是否有更高优先级的任务。如果有,当前低优先级的渲染会被暂停,先执行高优先级任务,之后再继续执行或重新执行。这就像看电影时,可以暂停电影先去开门,拿完快递再继续看电影。
并发模式的特点
渲染可中断 :React可能在渲染中途暂停,之后继续执行,甚至完全放弃重新开始
UI一致性保证 :即使渲染被中断,React也能保证UI显示一致
后台准备 :React可以在后台准备新屏幕,无需阻塞主线程
渐进式升级 :React 18的并发模式是渐进式的,默认不启用新特性
默认行为不变 重要的是,升级到React 18后,现有代码不会有任何变化 。并发模式默认是关闭的,只有当你使用新特性时才会启用。
二、Automatic Batching自动批处理 批处理概念 批处理是指React将多个状态更新合并到一次渲染中执行,以提升性能。
function handleClick ( ) { setCount (c => c + 1 ); setFlag (f => !f); }
React 18之前的限制 在React 18之前,批处理只发生在React事件处理器内部。Promise、setTimeout、原生事件等场景下,状态更新不会自动批处理:
function handleClick ( ) { setCount (c => c + 1 ); setFlag (f => !f); } setTimeout (() => { setCount (c => c + 1 ); setFlag (f => !f); }, 1000 );
React 18的改进 在React 18中,所有状态更新都会自动批处理,无论发生在什么场景下:
function handleClick ( ) { setCount (c => c + 1 ); setFlag (f => !f); } setTimeout (() => { setCount (c => c + 1 ); setFlag (f => !f); }, 1000 ); fetch ('/api' ).then (() => { setCount (c => c + 1 ); setFlag (f => !f); });
退出批处理 如果需要退出自动批处理,可以使用flushSync强制同步执行:
import { flushSync } from 'react-dom' ;function handleClick ( ) { flushSync (() => { setCounter (c => c + 1 ); }); flushSync (() => { setFlag (f => !f); }); }
三、Transitions:区分优先级 紧急更新vs过渡更新 React 18引入了更新的优先级概念:
紧急更新(Urgent updates) :打字、点击、拖动等直接交互,需要立即响应
过渡更新(Transition updates) :UI从一个视图过渡到另一个视图,可以有延迟
例如搜索框场景:用户输入时,输入框需要立即更新(紧急),但搜索结果列表可以延迟更新(过渡)。
startTransition API import { startTransition } from 'react' ;function SearchResults ( ) { const [query, setQuery] = useState ('' ); const [results, setResults] = useState ([]); function handleChange (e ) { const value = e.target .value ; setQuery (value); startTransition (() => { setResults (searchData (value)); }); } return ( <input value ={query} onChange ={handleChange} /> ); }
useTransition Hook useTransition Hook提供了isPending状态来跟踪过渡更新:
import { useTransition } from 'react' ;function SearchResults ( ) { const [query, setQuery] = useState ('' ); const [results, setResults] = useState ([]); const [isPending, startTransition] = useTransition (); function handleChange (e ) { const value = e.target .value ; setQuery (value); startTransition (() => { setResults (searchData (value)); }); } return ( <div > <input value ={query} onChange ={handleChange} /> {isPending && <Spinner /> } <ResultsList results ={results} /> </div > ); }
useDeferredValue useDeferredValue是另一种标记非紧急更新的方式,适用于无法修改源码的场景:
import { useDeferredValue } from 'react' ;function SearchResults ({ query } ) { const deferredQuery = useDeferredValue (query); const results = useMemo (() => { return searchData (deferredQuery); }, [deferredQuery]); return ( <div > <input value ={query} onChange ={e => setQuery(e.target.value)} /> <ResultsList results ={results} /> </div > ); }
四、Suspense增强 服务端渲染的Suspense React 18增强了Suspense在服务端渲染中的能力,支持流式SSR:
function App ( ) { return ( <Layout > <NavBar /> <Sidebar /> <RightPane > <Post /> <Suspense fallback ={ <Spinner /> }> <Comments /> </Suspense > </RightPane > </Layout > ); }
服务端首先返回不含Comments的HTML,Comments部分显示Spinner。当Comments数据准备好后,React会通过同一流发送并替换到相应位置。
与Transition结合 Suspense与Transition结合时,React会防止已显示内容被fallback替换:
function handleFilterChange (filter ) { startTransition (() => { setFilter (filter); }); } <Suspense fallback={<Loading /> }> <FilterResults filter ={filter} /> </Suspense >
五、新Hooks详解 useId:生成唯一ID useId用于在客户端和服务端生成相同的唯一ID,避免hydration不匹配:
import { useId } from 'react' ;function PasswordInput ( ) { const passwordId = useId (); const hintId = useId (); return ( <div > <label htmlFor ={passwordId} > Password</label > <input id ={passwordId} type ="password" /> <p id ={hintId} > Must be at least 8 characters</p > </div > ); }
生成的ID类似:r0:,格式确保了服务端和客户端一致。
useSyncExternalStore:外部状态管理 useSyncExternalStore用于在并发模式下安全读取外部数据源:
import { useSyncExternalStore } from 'react' ;function useStore (store ) { return useSyncExternalStore ( store.subscribe , store.getSnapshot , store.getServerSnapshot ); }
这个Hook主要是为第三方状态管理库(如Redux、Zustand)使用,普通应用开发者很少直接使用。
useInsertionEffect:CSS-in-JS优化 useInsertionEffect在DOM更新后、layout effects之前执行,主要供CSS-in-JS库使用:
import { useInsertionEffect } from 'react' ;function useStyledComponent (styles ) { useInsertionEffect (() => { const styleElement = document .createElement ('style' ); styleElement.textContent = styles; document .head .appendChild (styleElement); return () => { document .head .removeChild (styleElement); }; }, [styles]); }
六、新的Client/Server渲染API Client端API更新 import { render } from 'react-dom' ;render (<App /> , container);import { createRoot } from 'react-dom/client' ;const root = createRoot (container);root.render (<App /> ); root.unmount ();
Hydration更新 import { hydrate } from 'react-dom' ;hydrate (<App /> , container);import { hydrateRoot } from 'react-dom/client' ;hydrateRoot (container, <App /> );
Server端API更新 import { renderToPipeableStream } from 'react-dom/server' ;import { createReadableStreamFromReadable } from '@react-router/node' ;function handleRequest (req, res ) { const stream = renderToPipeableStream (<App /> , { onShellReady ( ) { res.statusCode = 200 ; res.setHeader ('Content-Type' , 'text/html' ); stream.pipe (createReadableStreamFromReadable (res)); }, }); } import { renderToReadableStream } from 'react-dom/server' ;const stream = await renderToReadableStream (<App /> );
七、Strict Mode增强 React 18的Strict Mode新增了开发模式下的检查,会自动模拟组件的挂载和卸载:
function App ( ) { useEffect (() => { console .log ('Effect执行' ); return () => console .log ('Cleanup' ); }, []); return <div > Hello</div > ; }
这有助于发现组件是否对多次挂载/卸载具有弹性,确保代码可以支持未来的可复用状态特性。
八、升级指南 安装React 18 npm install react@18 react-dom@18 npm install @types/react@18 @types/react-dom@18
迁移createRoot import { render } from 'react-dom' ;render (<App /> , document .getElementById ('root' ));import { createRoot } from 'react-dom/client' ;const root = createRoot (document .getElementById ('root' ));root.render (<App /> );
迁移hydrate import { hydrate } from 'react-dom' ;hydrate (<App /> , document .getElementById ('root' ));import { hydrateRoot } from 'react-dom/client' ;hydrateRoot (document .getElementById ('root' ), <App /> );
测试环境配置 globalThis.IS_REACT_ACT_ENVIRONMENT = true ;
九、重大变更与废弃API 废弃的API
废弃API
替代方案
ReactDOM.render
createRoot
ReactDOM.hydrate
hydrateRoot
ReactDOM.unmountComponentAtNode
root.unmount()
renderToNodeStream
renderToPipeableStream
其他重要变更
一致性Effect时序 :离散用户事件(如点击)触发的effect会同步执行
更严格的Hydration错误 :hydration不匹配现在被视为错误而非警告
组件可以返回undefined :不再警告,但建议使用linter防止忘记return
移除setState内存泄漏警告 :移除了对已卸载组件调用setState的警告
移除console.log抑制 :Strict Mode下两次渲染都会输出日志
十、总结 React 18是一次里程碑式的版本更新,核心变化包括:
Concurrent Mode :底层架构更新,支持渲染可中断
Automatic Batching :所有场景自动批处理,提升性能
Transitions :区分紧急和过渡更新,改善用户体验
Suspense增强 :支持流式SSR,加快首屏加载
新Hooks :useId、useSyncExternalStore、useInsertionEffect
Strict Mode增强 :帮助发现潜在的兼容性问题
虽然并发模式是底层变化,但大多数现有代码可以直接在React 18上运行。建议逐步引入新特性,享受性能提升带来的用户体验改善。
参考资料