React生命周期是组件在不同阶段调用的特殊方法,主要用于控制组件的行为和优化性能。以下是React生命周期的演进、使用方式和实例解读:
一、生命周期演进
1. Class组件生命周期(16.3版本前)
Mounting(挂载阶段):
constructor → componentWillMount → render → componentDidMount
Updating(更新阶段):
componentWillReceiveProps → shouldComponentUpdate → componentWillUpdate → render → componentDidUpdate
Unmounting(卸载阶段):
componentWillUnmount
2. Class组件生命周期(16.3+版本)
Mounting:
constructor → static getDerivedStateFromProps → render → componentDidMount
Updating:
static getDerivedStateFromProps → shouldComponentUpdate → render → getSnapshotBeforeUpdate → componentDidUpdate
Unmounting:
componentWillUnmount
3. 函数组件+Hooks
使用 useEffect、useState 等Hooks替代生命周期
二、常用生命周期方法详解
1. 挂载阶段
class Example extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
console.log('1. constructor');
}
static getDerivedStateFromProps(props, state) {
console.log('2. getDerivedStateFromProps');
// 根据props返回新的state
return null;
}
componentDidMount() {
console.log('4. componentDidMount');
// 适合进行网络请求、DOM操作、事件订阅
fetch('/api/data').then(response => {
this.setState({ data: response.data });
});
}
render() {
console.log('3. render');
return <div>{this.state.count}</div>;
}
}
2. 更新阶段
class Example extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
console.log('shouldComponentUpdate');
// 性能优化:决定是否重新渲染
return this.state.count !== nextState.count;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('getSnapshotBeforeUpdate');
// 获取更新前的DOM信息
return { scrollHeight: document.body.scrollHeight };
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('componentDidUpdate');
// 可以比较props/state变化
if (this.props.userId !== prevProps.userId) {
this.fetchData(this.props.userId);
}
// 使用snapshot恢复滚动位置
if (snapshot) {
const scrollDiff = document.body.scrollHeight - snapshot.scrollHeight;
window.scrollTo(0, window.scrollY + scrollDiff);
}
}
}
3. 卸载阶段
class Example extends React.Component {
componentWillUnmount() {
console.log('componentWillUnmount');
// 清理操作
clearInterval(this.timerId);
this.eventEmitter.removeListener('update', this.handleUpdate);
}
}
三、Hooks替代方案
import React, { useState, useEffect, useMemo } from 'react';
function FunctionalComponent({ userId }) {
const [data, setData] = useState(null);
const [count, setCount] = useState(0);
// componentDidMount + componentWillUnmount
useEffect(() => {
console.log('组件挂载');
// 订阅事件
const handleResize = () => console.log('resize');
window.addEventListener('resize', handleResize);
// 清理函数(componentWillUnmount)
return () => {
console.log('组件卸载');
window.removeEventListener('resize', handleResize);
};
}, []); // 空数组表示只在挂载和卸载时执行
// componentDidUpdate(监听特定props/state)
useEffect(() => {
if (userId) {
fetchData(userId);
}
}, [userId]); // 依赖数组:userId变化时执行
// 替代shouldComponentUpdate
const memoizedValue = useMemo(() => {
return computeExpensiveValue(count);
}, [count]);
// 替代getDerivedStateFromProps
const derivedState = useMemo(() => {
return propsToState(props);
}, [props.someProp]);
return <div>{data}</div>;
}
四、实用实例
1. 数据获取与取消
class UserProfile extends React.Component {
state = { user: null, loading: true };
componentDidMount() {
this.fetchUser(this.props.userId);
}
componentDidUpdate(prevProps) {
if (prevProps.userId !== this.props.userId) {
this.fetchUser(this.props.userId);
}
}
componentWillUnmount() {
// 防止组件卸载后setState
this._isMounted = false;
if (this.controller) {
this.controller.abort(); // 取消fetch请求
}
}
async fetchUser(userId) {
this.controller = new AbortController();
try {
const response = await fetch(`/api/users/${userId}`, {
signal: this.controller.signal
});
const user = await response.json();
if (this._isMounted !== false) {
this.setState({ user, loading: false });
}
} catch (error) {
if (error.name !== 'AbortError') {
console.error('Fetch error:', error);
}
}
}
}
2. 错误边界(Error Boundaries)
class ErrorBoundary extends React.Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
// 可以记录错误到监控系统
logErrorToService(error, errorInfo);
}
render() {
if (this.state.hasError) {
return <FallbackUI error={this.state.error} />;
}
return this.props.children;
}
}
3. 性能优化示例
class ExpensiveComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
// 深度比较对象(实际项目建议使用浅比较)
return !shallowEqual(this.props, nextProps);
}
render() {
return <div>{/* 昂贵的渲染 */}</div>;
}
}
// 使用React.memo的函数组件
const MemoizedComponent = React.memo(({ items }) => {
return items.map(item => <Item key={item.id} item={item} />);
}, (prevProps, nextProps) => {
// 自定义比较函数
return prevProps.items.length === nextProps.items.length;
});
五、最佳实践建议
避免在render中执行副作用
componentDidMount中进行异步操作
使用shouldComponentUpdate或React.memo优化性能
在componentWillUnmount中清理资源
优先使用函数组件+Hooks
getDerivedStateFromProps要谨慎使用,容易导致bug
六、生命周期图示
挂载时:
constructor
↓
getDerivedStateFromProps
↓
render
↓
componentDidMount
更新时:
getDerivedStateFromProps
↓
shouldComponentUpdate
↓
render
↓
getSnapshotBeforeUpdate
↓
componentDidUpdate
卸载时:
componentWillUnmount
理解React生命周期有助于编写更高效、更稳定的组件,但现代React开发更推荐使用函数组件和Hooks,它们提供了更简洁、更组合式的API。