React 17 がリリースされた。公式には「新機能のない」バージョンと呼ばれている。地味に聞こえるが、実際には React の将来のための重要な基盤を築いている。アップグレードの要点を整理する。
新しいAPIはないが、重要な変更あり
React 17 の中心的な変更:
- イベント委譲が document に紐付かなくなる:代わりに root DOM ノードに紐付く
- イベントプールの廃止:SyntheticEvent で
event.persist()が不要に - 新しい JSX Transform:
import Reactが不要に - 段階的なアップグレード:1 つのページ内で 2 つの React バージョンを同時に動かせる
イベントシステムの変更
javascript
// React 16:事件委托到 document
// <div id="root">...</div>
// 事件监听器绑在 document 上
// React 17:事件委托到 root DOM
// <div id="root">...</div>
// 事件监听器绑在 #root 上
// 影响:
// 1. 多个 React 版本共存时事件不会串
// 2. 嵌入到非 React 应用时更容易
// 3. 逐步迁移成为可能
イベントプーリングの廃止
javascript
// React 16:事件对象被池化,异步访问需要 persist
function handleClick(e) {
console.log(e.type); // 'click'
setTimeout(() => {
console.log(e.type); // undefined(已回收)
}, 100);
}
// React 16 的修复
function handleClick(e) {
e.persist(); // 保留事件对象
setTimeout(() => {
console.log(e.type); // 'click'
}, 100);
}
// React 17:不再需要 persist
function handleClick(e) {
console.log(e.type); // 'click'
setTimeout(() => {
console.log(e.type); // 'click'(正常工作)
}, 100);
}
新しいJSX Transform
javascript
// React 16:Babel 编译 JSX 需要 React 在作用域内
import React from 'react'; // 必须有这行
function App() {
return <div>Hello</div>;
}
// 编译为:
// React.createElement('div', null, 'Hello');
// React 17:自动导入,不需要 import React
// 不需要 import React from 'react'
function App() {
return <div>Hello</div>;
}
// 编译为:
// import { jsx as _jsx } from 'react/jsx-runtime';
// _jsx('div', { children: 'Hello' });
bash
# Babel 配置
# .babelrc
{
"presets": [
["@babel/preset-react", {
"runtime": "automatic" // 新的 JSX Transform
}]
]
}
useEffectクリーンアップのタイミング変更
javascript
// React 16:useEffect 清理函数在 componentDidUpdate 之后同步运行
// React 17:useEffect 清理函数异步运行(useEffect 返回时)
// 对于大多数代码没有影响
// 但如果 effect 和 layout effect 有依赖关系需要注意
useEffect(() => {
// 这个 effect
return () => {
// 清理函数在 React 17 中是异步运行的
// 但通常不需要关心这个
};
}, []);
// 如果确实需要同步清理,用 useLayoutEffect
useLayoutEffect(() => {
return () => {
// 同步运行
};
}, []);
アップグレード手順
bash
# 1. 更新依赖
npm install react@17 react-dom@17
# 2. 更新 Babel(如果用新的 JSX Transform)
npm install @babel/preset-react@latest
npm install @babel/core@latest
# 3. 运行代码检查
npx eslint src/ --ext .js,.jsx,.ts,.tsx
# 4. 运行测试
npm test
bash
# 使用官方升级脚本自动修复
npx react-codemod update-react-imports
# 这个脚本会:
# - 移除不需要的 import React from 'react'
# - 保留确实需要 React 的 import(如 React.Component)
互換性問題のトラブルシューティング
javascript
// 1. 依赖了 React 在全局作用域的代码会出问题
// 确保每个用到 React 的文件都有 import
// 2. React Native 还不支持 React 17
// 等待 React Native 更新
// 3. 第三方库兼容性
// 大部分库兼容,但可能需要更新版本
// 检查命令
npx react-codemod React-PropTypes-to-prop-types
段階的なアップグレード
javascript
// React 17 支持一个页面运行多个 React 版本
// 这意味着可以逐步迁移子应用
// 子应用 A:React 16
// 子应用 B:React 17
// 事件系统不再冲突
// 实际场景:微前端架构下的渐进升级
// 不需要一次性全部升级
まとめ
- React 17 は「橋渡しバージョン」であり、将来の Concurrency モードなどの機能の基盤となる
- イベント委譲が root DOM に変わり、複数バージョン共存の問題を解決した
- イベントプールの廃止でコードが簡潔になり、
persist()が不要になった - 新しい JSX Transform では
import React from 'react'が不要になった - アップグレード自体の影響は小さく、大半のプロジェクトは円滑に移行できる
