当前位置: 首页 > java >正文

React生命周期

下面,我们来系统的梳理关于 类组件生命周期 的基本知识点:


一、基础概念

1.1 什么是生命周期?

生命周期(Lifecycle)指的是组件从创建到销毁的整个过程。React类组件提供了一系列生命周期方法(也称为生命周期钩子),允许开发者在组件的不同阶段执行自定义代码。

1.2 生命周期的三个阶段

挂载阶段
更新阶段
卸载阶段
  1. 挂载阶段(Mounting):组件被创建并插入DOM
  2. 更新阶段(Updating):组件因props或state变化而重新渲染
  3. 卸载阶段(Unmounting):组件从DOM中移除

二、生命周期方法详解

2.1 挂载阶段(Mounting)

2.1.1 constructor()
  • 调用时机:组件初始化时调用
  • 主要用途
    • 初始化state
    • 绑定事件处理方法
  • 注意事项
    • 必须首先调用super(props)
    • 避免在此方法中引入副作用
class Counter extends React.Component {constructor(props) {super(props);this.state = { count: 0 };this.handleClick = this.handleClick.bind(this);}// ...
}
2.1.2 static getDerivedStateFromProps()
  • 调用时机:在调用render方法之前调用,并且在初始挂载及后续更新时都会被调用
  • 主要用途:使组件在props变化时更新state
  • 注意事项
    • 应返回一个对象来更新state,如果返回null则不更新
    • 此方法无权访问组件实例
class ScrollPosition extends React.Component {state = { scrollY: 0 };static getDerivedStateFromProps(props, state) {if (props.resetPosition) {return { scrollY: 0 };}return null;}// ...
}
2.1.3 render()
  • 调用时机:必须实现的方法,负责返回JSX
  • 主要用途:渲染组件UI
  • 注意事项
    • 应为纯函数,不修改组件状态
    • 不直接与浏览器交互
class Welcome extends React.Component {render() {return <h1>Hello, {this.props.name}</h1>;}
}
2.1.4 componentDidMount()
  • 调用时机:组件挂载后(插入DOM树中)立即调用
  • 主要用途
    • 执行DOM操作
    • 发起网络请求
    • 设置订阅
  • 注意事项:此方法中可以调用setState,但会触发额外渲染
class DataFetcher extends React.Component {componentDidMount() {fetch(this.props.url).then(res => res.json()).then(data => this.setState({ data }));}// ...
}

2.2 更新阶段(Updating)

2.2.1 static getDerivedStateFromProps()
  • 同挂载阶段,在每次更新前也会被调用
2.2.2 shouldComponentUpdate()
  • 调用时机:在重新渲染前触发
  • 主要用途:通过返回true/false控制组件是否更新
  • 注意事项
    • 默认返回true
    • 用于性能优化,避免不必要的渲染
    • 不建议深层比较
class PureComponentLike extends React.Component {shouldComponentUpdate(nextProps, nextState) {// 仅当count变化时才更新return nextState.count !== this.state.count;}// ...
}
2.2.3 render()
  • 同挂载阶段,负责渲染更新
2.2.4 getSnapshotBeforeUpdate()
  • 调用时机:在最近一次渲染输出(提交到DOM节点)之前调用
  • 主要用途:捕获渲染前的DOM信息(如滚动位置)
  • 注意事项
    • 返回值将作为参数传递给componentDidUpdate()
    • 此方法不常用
class ScrollList extends React.Component {getSnapshotBeforeUpdate(prevProps, prevState) {if (prevProps.items.length < this.props.items.length) {const list = this.listRef.current;return list.scrollHeight - list.scrollTop;}return null;}componentDidUpdate(prevProps, prevState, snapshot) {if (snapshot !== null) {const list = this.listRef.current;list.scrollTop = list.scrollHeight - snapshot;}}// ...
}
2.2.5 componentDidUpdate()
  • 调用时机:更新完成后立即调用(首次渲染不会调用)
  • 主要用途
    • 执行DOM操作
    • 进行网络请求(需比较props)
  • 注意事项
    • 可以调用setState,但必须包裹在条件语句中
    • 避免无限循环
class DataUpdater extends React.Component {componentDidUpdate(prevProps) {if (this.props.id !== prevProps.id) {fetch(`/data/${this.props.id}`).then(res => res.json()).then(data => this.setState({ data }));}}// ...
}

2.3 卸载阶段(Unmounting)

2.3.1 componentWillUnmount()
  • 调用时机:组件卸载及销毁之前调用
  • 主要用途
    • 清理定时器
    • 取消网络请求
    • 移除事件监听
    • 清理订阅
  • 注意事项:不应调用setState()
class TimerComponent extends React.Component {componentDidMount() {this.timerID = setInterval(() => this.tick(), 1000);}componentWillUnmount() {clearInterval(this.timerID);}// ...
}

2.4 错误处理

2.4.1 static getDerivedStateFromError()
  • 调用时机:后代组件抛出错误后调用
  • 主要用途:返回一个值以更新state,用于渲染降级UI
  • 注意事项
    • 在渲染阶段调用,不允许执行副作用
    • 仅用于捕获渲染/生命周期错误
class ErrorBoundary extends React.Component {state = { hasError: false };static getDerivedStateFromError(error) {return { hasError: true };}render() {if (this.state.hasError) {return <h1>发生错误,请重试</h1>;}return this.props.children;}
}
2.4.2 componentDidCatch()
  • 调用时机:后代组件抛出错误后调用
  • 主要用途
    • 记录错误信息
    • 执行副作用操作
  • 注意事项
    • 在提交阶段调用,可以执行副作用
    • 仅用于捕获渲染/生命周期错误
class ErrorBoundary extends React.Component {state = { hasError: false };componentDidCatch(error, info) {logErrorToService(error, info);this.setState({ hasError: true });}render() {if (this.state.hasError) {return <h1>发生错误,请重试</h1>;}return this.props.children;}
}

三、生命周期执行顺序

3.1 挂载过程

ConstructorgetDerivedStateFromPropsRendercomponentDidMount初始化props/state计算新state渲染DOM可触发setStateConstructorgetDerivedStateFromPropsRendercomponentDidMount

3.2 更新过程

getDerivedStateFromPropsshouldComponentUpdateRendergetSnapshotBeforeUpdatecomponentDidUpdate接收新props/state返回true则继续渲染UI传递快照可触发setStategetDerivedStateFromPropsshouldComponentUpdateRendergetSnapshotBeforeUpdatecomponentDidUpdate

3.3 卸载过程

componentWillUnmount执行清理操作componentWillUnmount

四、生命周期最佳实践

4.1 数据获取

  • 使用componentDidMount进行初始数据获取
  • 使用componentDidUpdate进行基于props变化的数据更新

4.2 性能优化

  • 使用shouldComponentUpdate避免不必要的渲染
  • 使用React.PureComponent自动浅比较props和state

4.3 资源清理

  • componentWillUnmount中清除所有副作用资源

4.4 错误处理

  • 使用错误边界(Error Boundaries)捕获组件树错误

五、生命周期与现代React

5.1 生命周期方法与Hooks对比

生命周期方法Hook 替代方案
constructoruseState 初始化
componentDidMountuseEffect(() => {}, [])
componentDidUpdateuseEffect(() => {}, [deps])
componentWillUnmountuseEffect 返回清理函数
shouldComponentUpdateReact.memo

5.2 废弃的生命周期方法

React 17+ 已废弃以下方法:

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

替代方案:

  • 使用static getDerivedStateFromProps替代componentWillReceiveProps
  • 使用getSnapshotBeforeUpdate替代componentWillUpdate

六、实战案例

6.1 数据获取组件

class DataLoader extends React.Component {state = {data: null,isLoading: true,error: null};componentDidMount() {this.fetchData();}componentDidUpdate(prevProps) {if (prevProps.url !== this.props.url) {this.fetchData();}}componentWillUnmount() {// 如果请求未完成,可以在这里中止}async fetchData() {this.setState({ isLoading: true, error: null });try {const response = await fetch(this.props.url);const data = await response.json();this.setState({ data, isLoading: false });} catch (error) {this.setState({ error, isLoading: false });}}render() {const { data, isLoading, error } = this.state;if (isLoading) return <Spinner />;if (error) return <ErrorDisplay error={error} />;return <DataView data={data} />;}
}

6.2 滚动位置保持组件

class ScrollPreserver extends React.Component {listRef = React.createRef();getSnapshotBeforeUpdate(prevProps) {if (prevProps.items.length < this.props.items.length) {const list = this.listRef.current;return list.scrollHeight - list.scrollTop;}return null;}componentDidUpdate(prevProps, prevState, snapshot) {if (snapshot !== null) {const list = this.listRef.current;list.scrollTop = list.scrollHeight - snapshot;}}render() {return (<div ref={this.listRef} style={{ overflowY: 'scroll', height: 300 }}>{this.props.items.map(item => (<div key={item.id}>{item.text}</div>))}</div>);}
}

6.3 错误边界组件

class ErrorBoundary extends React.Component {state = { hasError: false, error: null, errorInfo: null };static getDerivedStateFromError(error) {return { hasError: true };}componentDidCatch(error, errorInfo) {this.setState({ error, errorInfo });logErrorToService(error, errorInfo);}render() {if (this.state.hasError) {return (<div className="error-boundary"><h2>应用发生错误</h2><details style={{ whiteSpace: 'pre-wrap' }}>{this.state.error && this.state.error.toString()}<br />{this.state.errorInfo.componentStack}</details><button onClick={() => window.location.reload()}>重新加载</button></div>);}return this.props.children;}
}

七、常见问题与解决方案

7.1 避免内存泄漏

问题:组件卸载后调用setState

componentDidMount() {this.timer = setInterval(() => {this.setState({ time: Date.now() });}, 1000);
}componentWillUnmount() {clearInterval(this.timer);
}

7.2 避免无限循环

问题:在componentDidUpdate中无条件调用setState

componentDidUpdate(prevProps) {// 错误:无条件调用setState// this.setState({ count: this.state.count + 1 });// 正确:添加条件判断if (this.props.id !== prevProps.id) {this.fetchData();}
}

7.3 正确处理异步操作

问题:组件卸载后处理异步回调

componentDidMount() {this._isMounted = true;fetchData().then(data => {if (this._isMounted) {this.setState({ data });}});
}componentWillUnmount() {this._isMounted = false;
}

八、生命周期图解总结

错误处理
卸载阶段
更新阶段
挂载阶段
true
false
getDerivedStateFromError
componentDidCatch
componentWillUnmount
componentDidUpdate
getDerivedStateFromProps
componentDidMount
constructor
getDerivedStateFromProps
render
shouldComponentUpdate
render
getSnapshotBeforeUpdate
跳过渲染
render

总结

  1. 理解三大阶段:挂载、更新、卸载
  2. 掌握核心方法
    • constructor:初始化状态和绑定方法
    • componentDidMount:执行副作用操作
    • shouldComponentUpdate:优化性能
    • componentDidUpdate:响应更新
    • componentWillUnmount:清理资源
  3. 使用错误边界:捕获组件树中的错误
  4. 遵循最佳实践
    • 避免在render中执行副作用
    • 在componentWillUnmount中清理资源
    • 使用条件判断防止无限循环
  5. 与现代React结合
    • 了解生命周期方法与Hooks的对应关系
    • 避免使用已废弃的生命周期方法
http://www.xdnf.cn/news/15764.html

相关文章:

  • 【Bluedroid】btif_av_sink_execute_service之服务器启用源码流程解析
  • 一动一静皆消耗——IC设计之低功耗技术(Low Power Design)
  • install_arm_docker.sh
  • Redis性能测试全攻略:工具实操与性能优化指南
  • 安装单机版本Redis
  • 2025第15届上海国际生物发酵展:聚焦合成生物与绿色制造,共启生物经济新时代
  • 在 .NET Core 中创建 Web Socket API
  • Spring AI 1.0版本 + 千问大模型之文本对话
  • FPGA自学——二选一多路选择器
  • 南洋理工空中导航零样本迁移与泛化!VLFly:基于开放词汇目标理解的无人机视觉语言导航
  • 1. Spring AI概述
  • 论文略读:Are Large Language Models In-Context Graph Learners?
  • 100条常用SQL语句
  • javaweb的几大常见漏洞
  • YOLOv11改进 | DWRSeg扩张式残差助力小目标检测
  • 3.条件判断:让程序学会做选择
  • gitlab+jenkins
  • 【数据结构】栈(stack)
  • Uniapp之自定义图片预览
  • Linux --进程信号
  • 初识C++——开启新旅途
  • 【51单片机学习】LED、独立按键
  • ENSP路由综合实验 + 思科(cisco)/华为(ensp)链路聚合实验
  • C++中的vector(2)
  • 基于Python的口腔正畸健康教育聊天机器人开发与评估研究
  • PyCharm + AI 辅助编程
  • 深度学习图像分类数据集—六十种植物病害分类
  • 基于单片机宠物喂食器/智能宠物窝/智能饲养
  • Typecho博客Ajax评论功能实现全攻略
  • 车载诊断架构 --- OEM对于DTC相关参数得定义