关于 react-modernization
react-modernization 是为 React 应用升级与重构提供的技能指南,面向维护旧版应用或采用新 React 功能的开发者。内容涵盖类组件迁移、并发特性、自动化 codemod、性能优化和 TypeScript 迁移。
主要功能
- 01React 版本升级指导
- 02类组件迁移至 Hooks
- 03React 18 并发特性采用
- 04使用 codemod 自动重构
- 05性能优化与代码拆分
- 06迁移至 TypeScript
适用场景
- 01逐步升级旧版 React 应用
- 02将生命周期逻辑改写为 Hooks
- 03用 codemod 批量处理重复迁移
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ ovachiever/ droid-tings --skill react-modernization安装后在 Claude Code 中输入/react-modernization确认已加载。
详细介绍
React 现代化
掌握 React 版本升级、从类组件迁移到 Hooks、采用并发功能,以及使用 codemod 自动转换。
何时使用此技能
- 将 React 应用升级到最新版本
- 将类组件迁移为使用 Hooks 的函数组件
- 采用 React 并发功能(Suspense、transitions)
- 使用 codemod 自动重构
- 现代化状态管理模式
- 迁移到 TypeScript
- 使用 React 18+ 功能提升性能
版本升级路径
React 16 → 17 → 18
各版本的破坏性变更:
React 17:
- 事件委托变更
- 不再进行事件池化
- Effect 清理时机
- JSX 转换(无需导入 React)
React 18:
- 自动批处理
- 并发渲染
- Strict Mode 变更(双重调用)
- 新的根 API
- 服务端 Suspense
从类组件迁移到 Hooks
状态管理
// Before: Class component
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
name: ''
};
}
increment = () => {
this.setState({ count: this.state.count + 1 });
}
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Increment</button>
</div>
);
}
}
// After: Functional component with hooks
function Counter() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
将生命周期方法迁移到 Hooks
// Before: Lifecycle methods
class DataFetcher extends React.Component {
state = { data: null, loading: true };
componentDidMount() {
this.fetchData();
}
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
this.fetchData();
}
}
componentWillUnmount() {
this.cancelRequest();
}
fetchData = async () => {
const data = await fetch(`/api/${this.props.id}`);
this.setState({ data, loading: false });
};
cancelRequest = () => {
// Cleanup
};
render() {
if (this.state.loading) return <div>Loading...</div>;
return <div>{this.state.data}</div>;
}
}
// After: useEffect hook
function DataFetcher({ id }) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
const fetchData = async () => {
try {
const response = await fetch(`/api/${id}`);
const result = await response.json();
if (!cancelled) {
setData(result);
setLoading(false);
}
} catch (error) {
if (!cancelled) {
console.error(error);
}
}
};
fetchData();
// Cleanup function
return () => {
cancelled = true;
};
}, [id]); // Re-run when id changes
if (loading) return <div>Loading...</div>;
return <div>{data}</div>;
}
将 Context 和 HOC 迁移到 Hooks
// Before: Context consumer and HOC
const ThemeContext = React.createContext();
class ThemedButton extends React.Component {
static contextType = ThemeContext;
render() {
return (
<button style={{ background: this.context.theme }}>
{this.props.children}
</button>
);
}
}
// After: useContext hook
function ThemedButton({ children }) {
const { theme } = useContext(ThemeContext);
return (
<button style={{ background: theme }}>
{children}
</button>
);
}
// Before: HOC for data fetching
function withUser(Component) {
return class extends React.Component {
state = { user: null };
componentDidMount() {
fetchUser().then(user => this.setState({ user }));
}
render() {
return <Component {...this.props} user={this.state.user} />;
}
};
}
// After: Custom hook
function useUser() {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser().then(setUser);
}, []);
return user;
}
function UserProfile() {
const user = useUser();
if (!user) return <div>Loading...</div>;
return <div>{user.name}</div>;
}
React 18 并发功能
新的根 API
// Before: React 17
import ReactDOM from 'react-dom';
ReactDOM.render(<App />, document.getElementById('root'));
// After: React 18
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
自动批处理
// React 18: All updates are batched
function handleClick() {
setCount(c => c + 1);
setFlag(f => !f);
// Only one re-render (batched)
}
// Even in async:
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// Still batched in React 18!
}, 1000);
// Opt out if needed
import { flushSync } from 'react-dom';
flushSync(() => {
setCount(c => c + 1);
});
// Re-render happens here
setFlag(f => !f);
// Another re-render
Transitions
import { useState, useTransition } from 'react';
function SearchResults() {
const [query, setQuery] = useState('');
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleChange = (e) => {
// Urgent: Update input immediately
setQuery(e.target.value);
// Non-urgent: Update results (can be interrupted)
startTransition(() => {
setResults(searchResults(e.target.value));
});
};
return (
<>
<input value={query} onChange={handleChange} />
{isPending && <Spinner />}
<Results data={results} />
</>
);
}
用于数据获取的 Suspense
import { Suspense } from 'react';
// Resource-based data fetching (with React 18)
const resource = fetchProfileData();
function ProfilePage() {
return (
<Suspense fallback={<Loading />}>
<ProfileDetails />
<Suspense fallback={<Loading />}>
<ProfileTimeline />
</Suspense>
</Suspense>
);
}
function ProfileDetails() {
// This will suspend if data not ready
const user = resource.user.read();
return <h1>{user.name}</h1>;
}
function ProfileTimeline() {
const posts = resource.posts.read();
return <Timeline posts={posts} />;
}
使用 Codemod 自动化
运行 React Codemod
# Install jscodeshift
npm install -g jscodeshift
# React 16.9 codemod (rename unsafe lifecycle methods)
npx react-codeshift <transform> <path>
# Example: Rename UNSAFE_ methods
npx react-codeshift --parser=tsx \
--transform=react-codeshift/transforms/rename-unsafe-lifecycles.js \
src/
# Update to new JSX Transform (React 17+)
npx react-codeshift --parser=tsx \
--transform=react-codeshift/transforms/new-jsx-transform.js \
src/
# Class to Hooks (third-party)
npx codemod react/hooks/convert-class-to-function src/
自定义 Codemod 示例
// custom-codemod.js
module.exports = function(file, api) {
const j = api.jscodeshift;
const root = j(file.source);
// Find setState calls
root.find(j.CallExpression, {
callee: {
type: 'MemberExpression',
property: { name: 'setState' }
}
}).forEach(path => {
// Transform to useState
// ... transformation logic
});
return root.toSource();
};
// Run: jscodeshift -t custom-codemod.js src/
性能优化
useMemo 和 useCallback
function ExpensiveComponent({ items, filter }) {
// Memoize expensive calculation
const filteredItems = useMemo(() => {
return items.filter(item => item.category === filter);
}, [items, filter]);
// Memoize callback to prevent child re-renders
const handleClick = useCallback((id) => {
console.log('Clicked:', id);
}, []); // No dependencies, never changes
return (
<List items={filteredItems} onClick={handleClick} />
);
}
// Child component with memo
const List = React.memo(({ items, onClick }) => {
return items.map(item => (
<Item key={item.id} item={item} onClick={onClick} />
));
});
代码拆分
import { lazy, Suspense } from 'react';
// Lazy load components
const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));
function App() {
return (
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
迁移到 TypeScript
// Before: JavaScript
function Button({ onClick, children }) {
return <button onClick={onClick}>{children}</button>;
}
// After: TypeScript
interface ButtonProps {
onClick: () => void;
children: React.ReactNode;
}
function Button({ onClick, children }: ButtonProps) {
return <button onClick={onClick}>{children}</button>;
}
// Generic components
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <>{items.map(renderItem)}</>;
}
迁移检查清单
### Pre-Migration
- [ ] Update dependencies incrementally (not all at once)
- [ ] Review breaking changes in release notes
- [ ] Set up testing suite
- [ ] Create feature branch
### Class → Hooks Migration
- [ ] Identify class components to migrate
- [ ] Start with leaf components (no children)
- [ ] Convert state to useState
- [ ] Convert lifecycle to useEffect
- [ ] Convert context to useContext
- [ ] Extract custom hooks
- [ ] Test thoroughly
### React 18 Upgrade
- [ ] Update to React 17 first (if needed)
- [ ] Update react and react-dom to 18
- [ ] Update @types/react if using TypeScript
- [ ] Change to createRoot API
- [ ] Test with StrictMode (double invocation)
- [ ] Address concurrent rendering issues
- [ ] Adopt Suspense/Transitions where beneficial
### Performance
- [ ] Identify performance bottlenecks
- [ ] Add React.memo where appropriate
- [ ] Use useMemo/useCallback for expensive operations
- [ ] Implement code splitting
- [ ] Optimize re-renders
### Testing
- [ ] Update test utilities (React Testing Library)
- [ ] Test with React 18 features
- [ ] Check for warnings in console
- [ ] Performance testing
资源
- references/breaking-changes.md:各版本的破坏性变更
- references/codemods.md:Codemod 使用指南
- references/hooks-migration.md:全面的 Hooks 模式
- references/concurrent-features.md:React 18 并发功能
- assets/codemod-config.json:Codemod 配置
- assets/migration-checklist.md:分步检查清单
- scripts/apply-codemods.sh:自动化 Codemod 脚本
最佳实践
- 渐进式迁移:不要一次性迁移所有内容
- 充分测试:在每个步骤进行全面测试
- 使用 Codemod:自动化重复的转换
- 从简单开始:先从叶子组件着手
- 利用 StrictMode:尽早发现问题
- 监控性能:迁移前后都进行测量
- 记录变更:保留迁移日志
常见陷阱
- 忘记 useEffect 依赖项
- 过度使用 useMemo/useCallback
- 未在 useEffect 中处理清理
- 混用类组件和函数组件模式
- 忽略 StrictMode 警告
- 对破坏性变更的影响做出错误假设
评论
支持 Markdown