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

React学习教程,从入门到精通,React 组件生命周期详解(适用于 React 16.3+,推荐函数组件 + Hooks)(17)

React 组件生命周期详解(适用于 React 16.3+,推荐函数组件 + Hooks)

⚠️ 重要说明:自 React 16.3 起,部分生命周期方法被标记为不安全(如 componentWillMount, componentWillReceiveProps, componentWillUpdate),并在 React 17 中被移除。目前推荐使用函数组件 + Hooks(如 useEffect, useLayoutEffect)来管理副作用和生命周期。但为了知识完整性,本文将同时介绍类组件的完整生命周期现代函数组件的等效写法


一、React 类组件生命周期(完整版)

1. 挂载阶段(Mounting)

生命周期方法说明是否常用
constructor()构造函数,初始化 state 和绑定方法
static getDerivedStateFromProps()静态方法,根据 props 更新 state(不常用)⚠️
render()渲染 UI,必须实现
componentDidMount()组件挂载后调用,适合发起网络请求、订阅事件等

2. 更新阶段(Updating)

生命周期方法说明是否常用
static getDerivedStateFromProps()同上,在 props 更新时也会调用⚠️
shouldComponentUpdate()判断是否需要重新渲染,默认返回 true✅(优化)
render()重新渲染
getSnapshotBeforeUpdate()在 DOM 更新前获取快照(如滚动位置)⚠️
componentDidUpdate()DOM 更新后调用,可执行副作用操作(如更新 DOM、发请求)

3. 卸载阶段(Unmounting)

生命周期方法说明是否常用
componentWillUnmount()组件卸载前调用,用于清理定时器、取消订阅等

4. 错误处理阶段(Error Handling)

生命周期方法说明是否常用
static getDerivedStateFromError()捕获子组件错误并更新 state 显示降级 UI
componentDidCatch()捕获错误并记录日志

二、完整类组件生命周期案例(带详细注释)

import React from 'react';class LifecycleDemo extends React.Component {constructor(props) {super(props);console.log('[constructor] 构造函数:初始化 state 和绑定方法');this.state = {count: 0,error: null};// 绑定方法(或使用箭头函数)this.handleClick = this.handleClick.bind(this);}// ⚠️ 静态方法:根据 props 派生 state(不推荐滥用)static getDerivedStateFromProps(props, state) {console.log('[getDerivedStateFromProps] 根据 props 更新 state(谨慎使用)');// 示例:如果 props.reset 为 true,则重置 countif (props.reset && state.count !== 0) {return { count: 0 };}return null; // 返回 null 表示不更新 state}// ✅ 组件挂载后:适合发起网络请求、订阅事件componentDidMount() {console.log('[componentDidMount] 组件已挂载到 DOM,可执行副作用操作');// 模拟网络请求this.timer = setInterval(() => {console.log('定时器运行中...');}, 3000);// 模拟聚焦this.inputRef && this.inputRef.focus();}// ✅ 性能优化:判断是否需要重新渲染shouldComponentUpdate(nextProps, nextState) {console.log('[shouldComponentUpdate] 判断是否需要更新,避免不必要的渲染');// 示例:只有当 count 改变时才更新if (this.state.count === nextState.count) {return false; // 不更新}return true; // 更新}// ⚠️ DOM 更新前获取快照(如滚动位置)getSnapshotBeforeUpdate(prevProps, prevState) {console.log('[getSnapshotBeforeUpdate] DOM 更新前获取快照');// 示例:获取滚动位置if (prevState.count !== this.state.count) {const scrollPos = window.scrollY;return scrollPos; // 返回值会作为第三个参数传给 componentDidUpdate}return null;}// ✅ DOM 更新后:可操作 DOM 或发起请求componentDidUpdate(prevProps, prevState, snapshot) {console.log('[componentDidUpdate] DOM 已更新,可执行副作用');if (snapshot !== null) {console.log('更新前的滚动位置:', snapshot);}// 示例:如果 count 改变,发送数据到服务器if (prevState.count !== this.state.count) {console.log(`计数更新为:${this.state.count}`);}}// ✅ 组件卸载前:清理定时器、取消订阅componentWillUnmount() {console.log('[componentWillUnmount] 组件即将卸载,清理副作用');if (this.timer) {clearInterval(this.timer);console.log('定时器已清理');}}// ✅ 错误边界:捕获子组件错误static getDerivedStateFromError(error) {console.log('[getDerivedStateFromError] 捕获子组件错误,更新 state 显示降级 UI');return { error: error.toString() };}// ✅ 错误边界:记录错误日志componentDidCatch(error, errorInfo) {console.log('[componentDidCatch] 捕获错误并记录日志');console.error('错误信息:', error);console.error('错误堆栈:', errorInfo.componentStack);// 可以发送错误报告到服务器}// 事件处理函数handleClick() {this.setState({ count: this.state.count + 1 });}// 渲染函数(必须实现)render() {console.log('[render] 渲染 UI');if (this.state.error) {return <h2>发生错误:{this.state.error}</h2>;}return (<div style={{ padding: '20px', border: '1px solid #ccc' }}><h2>React 类组件生命周期演示</h2><p>当前计数:{this.state.count}</p><button onClick={this.handleClick}>点击 +1</button><br /><br /><input ref={el => this.inputRef = el} placeholder="自动聚焦的输入框" style={{ marginTop: '10px' }}/>{/* 故意制造错误 */}{/* <button onClick={() => { throw new Error('故意制造的错误!'); }}>点击制造错误</button> */}</div>);}
}export default LifecycleDemo;

三、现代 React:函数组件 + Hooks 生命周期等效写法

Hooks 对应关系:

类组件生命周期函数组件 Hooks 等效写法
constructoruseState, useRef 初始化
componentDidMountuseEffect(() => {}, [])
componentDidUpdateuseEffect(() => {})(依赖变化时)
componentWillUnmountuseEffect(() => { return () => {} }, [])
shouldComponentUpdateReact.memo, useMemo, useCallback
错误边界仍需类组件(Hooks 无直接等效)

函数组件完整案例:

import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react';function HooksLifecycleDemo({ reset }) {const [count, setCount] = useState(0);const [error, setError] = useState(null);const inputRef = useRef(null);const timerRef = useRef(null);// ✅ 等效于 componentDidMount + componentDidUpdate + componentWillUnmountuseEffect(() => {console.log('[useEffect] 组件挂载或 reset 变化时执行');// 模拟 componentDidMounttimerRef.current = setInterval(() => {console.log('定时器运行中...');}, 3000);// 聚焦输入框if (inputRef.current) {inputRef.current.focus();}// ✅ 等效于 componentWillUnmount:返回清理函数return () => {console.log('[useEffect cleanup] 组件卸载或 reset 变化前清理');if (timerRef.current) {clearInterval(timerRef.current);console.log('定时器已清理');}};}, [reset]); // 依赖数组:仅在 reset 变化或首次挂载时执行// ✅ 等效于 componentDidUpdate(监听 count 变化)useEffect(() => {console.log('[useEffect count] count 发生变化:', count);// 可以在这里发送数据到服务器if (count > 0) {console.log(`计数更新为:${count}`);}}, [count]); // 仅当 count 变化时执行// ✅ 等效于 shouldComponentUpdate - 使用 useMemo 优化计算const expensiveValue = useMemo(() => {console.log('[useMemo] 计算昂贵值(仅在 count 变化时重新计算)');return count * 1000;}, [count]);// ✅ 等效于 shouldComponentUpdate - 使用 useCallback 缓存函数const handleClick = useCallback(() => {setCount(prev => prev + 1);}, []); // 依赖为空数组,函数不会重新创建// 渲染函数if (error) {return <h2>发生错误:{error}</h2>;}return (<div style={{ padding: '20px', border: '1px solid #ccc', marginTop: '20px' }}><h2>React 函数组件 + Hooks 生命周期演示</h2><p>当前计数:{count}</p><p>昂贵计算值:{expensiveValue}</p><button onClick={handleClick}>点击 +1</button><br /><br /><input ref={inputRef} placeholder="自动聚焦的输入框" style={{ marginTop: '10px' }}/></div>);
}// 使用示例(父组件)
function App() {const [resetFlag, setResetFlag] = useState(false);return (<div><h1>React 生命周期演示</h1><button onClick={() => setResetFlag(!resetFlag)}>切换 Reset ({resetFlag ? 'true' : 'false'})</button><hr /><LifecycleDemo reset={resetFlag} /><hr /><HooksLifecycleDemo reset={resetFlag} /></div>);
}export default App;

四、生命周期执行顺序总结

类组件挂载顺序:

constructor → getDerivedStateFromProps → render → componentDidMount

类组件更新顺序(props 或 state 变化):

getDerivedStateFromProps → shouldComponentUpdate → render → getSnapshotBeforeUpdate → componentDidUpdate

类组件卸载顺序:

componentWillUnmount

函数组件执行顺序:

  • 初始化:useStateuseMemouseCallbackrenderuseEffect(依赖为空)
  • 更新:renderuseEffect(依赖变化)→ 清理上一次 effect → 执行新的 effect
  • 卸载:清理所有 effect

五、最佳实践建议

  1. 优先使用函数组件 + Hooks:更简洁、易测试、逻辑复用方便。
  2. 避免使用 getDerivedStateFromProps:除非必要,否则容易导致 bug。
  3. 合理使用 useEffect 依赖数组:避免无限循环或遗漏依赖。
  4. 及时清理副作用:在 useEffect 返回函数中清理定时器、订阅等。
  5. 性能优化:使用 React.memo, useMemo, useCallback 避免不必要的渲染。
  6. 错误边界:对于可能出错的组件,用类组件包裹提供降级 UI。

六、常见问题

Q:为什么我的 useEffect 执行了两次?
A:在开发模式下,React 会故意卸载并重新挂载组件以帮助发现清理问题。生产环境不会。

Q:如何模拟 shouldComponentUpdate?
A:使用 React.memo 包裹组件,或使用 useMemo/useCallback 优化子组件。

Q:Hooks 能完全替代类组件吗?
A:几乎可以,除了错误边界目前仍需类组件实现。


通过以上完整案例和注释,你应该对 React 生命周期有了全面理解。在实际开发中,推荐使用函数组件 + Hooks 方案,它更符合现代 React 开发趋势!


文章转载自:

http://XKg257WR.rrxmm.cn
http://tARY13NP.rrxmm.cn
http://w6J04eNl.rrxmm.cn
http://S4m8jxld.rrxmm.cn
http://bCRokZ6O.rrxmm.cn
http://QDc24BC8.rrxmm.cn
http://9ZaRYBK3.rrxmm.cn
http://U4uFImCa.rrxmm.cn
http://NEYI5oPO.rrxmm.cn
http://tTUCdXtW.rrxmm.cn
http://Fmpxyypp.rrxmm.cn
http://LKKL6PG0.rrxmm.cn
http://LMbNdUT5.rrxmm.cn
http://Zb3iGMuz.rrxmm.cn
http://9iXgFWW0.rrxmm.cn
http://X5f4Q6g8.rrxmm.cn
http://V37mxIuC.rrxmm.cn
http://KSaGOYOD.rrxmm.cn
http://Um59KCWO.rrxmm.cn
http://nTdjFxq7.rrxmm.cn
http://DWhCg8NI.rrxmm.cn
http://GE0VOZPh.rrxmm.cn
http://FaJ1GQco.rrxmm.cn
http://GLR9aBKo.rrxmm.cn
http://AAqJ16S0.rrxmm.cn
http://kqJRqLeD.rrxmm.cn
http://pp31NOwJ.rrxmm.cn
http://UE87yYQz.rrxmm.cn
http://LGNGbxyV.rrxmm.cn
http://m7vF5CUQ.rrxmm.cn
http://www.dtcms.com/a/378716.html

相关文章:

  • EFK+DeepSeek 智能运维方案:技术架构与实施步骤
  • 零基础快速了解掌握Linux防火墙-Iptables
  • python---PyInstaller(将Python脚本打包为可执行文件)
  • Python 数据类型转换完全指南:方法与最佳实践
  • 冷压对辊矫平机:金属板材的“应力按摩师”
  • Django REST Framework响应类Response详解
  • 一款.NET开发的AI无损放大工具
  • linux安装远程桌面图形化界面以及root登录
  • 短视频流量算法
  • 前端网络性能优化实践:从 HTTP 请求到 HTTPS 与 HTTP/2 升级
  • 37.循环神经网络:让AI理解序列
  • 合集:Git代码托管平台
  • Python 高效实现 Word 转 PDF:告别 Office 依赖
  • flutter配置Android gradle kts 8.0 的打包名称
  • 从零到一使用Linux+Nginx+MySQL+PHP搭建的Web网站服务器架构环境——LNMP(下)
  • 从iPhone 17取消SIM卡槽,看企业如何告别“数据孤岛”
  • Docker入门指南:CentOS 7 安装与阿里云镜像加速配置
  • 【Python自动化】 22 Python os 库详解
  • 智能投影仪技术解析:从显示工具到智慧影音终端的演进
  • 下一代社媒运营工具:亚矩阵云手机集成AIGC与数字人技术引领内容革命
  • 在Excel中删除大量间隔空白行
  • Android Studio Meerkat | 2024.3.1 Gradle Tasks不展示
  • 新版Android Studio能打包但无法run ‘app‘,编译通过后手机中没有安装,顶部一直转圈
  • CSS 伪类选择器
  • 2年1170万!39岁的霍福德,还有多少油?
  • IsaacSim Segmentation
  • CLIP、DALL·E 1的解读
  • Go 语言开发环境安装与 GOPROXY 镜像配置(含依赖管理与版本切换技巧)
  • 麒麟V10 + Docker部署KingbaseES数据库实战教程
  • 比亚迪新版五合一登陆、签到、查询、迁移