React 生命周期完整指南

React 生命周期完整指南

1. 生命周期概述

1.1 React 16.3 之前的生命周期

  • 初始化阶段
    • constructor
    • componentWillMount
    • render
    • componentDidMount
  • 更新阶段
    • componentWillReceiveProps
    • shouldComponentUpdate
    • componentWillUpdate
    • render
    • componentDidUpdate
  • 卸载阶段
    • componentWillUnmount

1.2 React 16.3 之后的生命周期

  • 初始化阶段
    • constructor
    • getDerivedStateFromProps
    • render
    • componentDidMount
  • 更新阶段
    • getDerivedStateFromProps
    • shouldComponentUpdate
    • render
    • getSnapshotBeforeUpdate
    • componentDidUpdate
  • 卸载阶段
    • componentWillUnmount

2. 详细说明

2.1 挂载阶段

jsx 复制代码
class MyComponent extends React.Component {
  // 1. 构造函数
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
    console.log('1. constructor');
  }

  // 2. 派生状态
  static getDerivedStateFromProps(props, state) {
    console.log('2. getDerivedStateFromProps');
    return null;
  }

  // 3. 渲染
  render() {
    console.log('3. render');
    return <div>{this.state.count}</div>;
  }

  // 4. 挂载完成
  componentDidMount() {
    console.log('4. componentDidMount');
  }
}

2.2 更新阶段

jsx 复制代码
class UpdateComponent extends React.Component {
  // 1. 派生状态
  static getDerivedStateFromProps(props, state) {
    console.log('1. getDerivedStateFromProps');
    return null;
  }

  // 2. 是否应该更新
  shouldComponentUpdate(nextProps, nextState) {
    console.log('2. shouldComponentUpdate');
    return true;
  }

  // 3. 渲染
  render() {
    console.log('3. render');
    return <div>{this.state.count}</div>;
  }

  // 4. 获取更新前的快照
  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log('4. getSnapshotBeforeUpdate');
    return null;
  }

  // 5. 更新完成
  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log('5. componentDidUpdate');
  }
}

2.3 卸载阶段

jsx 复制代码
class UnmountComponent extends React.Component {
  componentWillUnmount() {
    console.log('componentWillUnmount');
    // 清理工作:取消订阅、清除定时器等
  }
}

3. 新旧生命周期对比

3.1 被移除的生命周期方法

componentWillMount
jsx 复制代码
// 旧版本
componentWillMount() {
  // 组件即将挂载
  // 问题:可能会导致服务器端渲染问题
}

// 新版本替代方案
componentDidMount() {
  // 组件已挂载
  // 建议在这里进行异步操作
}
componentWillReceiveProps
jsx 复制代码
// 旧版本
componentWillReceiveProps(nextProps) {
  // 即将接收新的 props
  if (nextProps.value !== this.props.value) {
    this.setState({ value: nextProps.value });
  }
}

// 新版本替代方案
static getDerivedStateFromProps(props, state) {
  if (props.value !== state.value) {
    return { value: props.value };
  }
  return null;
}
componentWillUpdate
jsx 复制代码
// 旧版本
componentWillUpdate(nextProps, nextState) {
  // 即将更新
  // 问题:可能会导致异步渲染问题
}

// 新版本替代方案
getSnapshotBeforeUpdate(prevProps, prevState) {
  // 获取更新前的快照
  return null;
}

3.2 新增的生命周期方法

getDerivedStateFromProps
jsx 复制代码
class Example extends React.Component {
  static getDerivedStateFromProps(props, state) {
    // 返回新状态或 null
    if (props.currentRow !== state.lastRow) {
      return {
        isScrollingDown: props.currentRow > state.lastRow,
        lastRow: props.currentRow
      };
    }
    return null;
  }
}
getSnapshotBeforeUpdate
jsx 复制代码
class ScrollingList extends React.Component {
  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 捕获更新前的信息
    if (prevProps.list.length < this.props.list.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;
    }
  }
}

4. 生命周期最佳实践

4.1 常见用途

jsx 复制代码
class BestPractices extends React.Component {
  constructor(props) {
    super(props);
    // 初始化状态
    // 绑定方法
  }

  static getDerivedStateFromProps(props, state) {
    // 用于状态的派生
    // 不要在这里执行副作用
    return null;
  }

  componentDidMount() {
    // 适合执行副作用
    // - 网络请求
    // - DOM 操作
    // - 订阅
  }

  shouldComponentUpdate(nextProps, nextState) {
    // 性能优化
    // 返回 false 可以阻止更新
    return true;
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 获取 DOM 更新前的信息
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // 更新后的操作
    // 可以执行副作用
    // 注意避免无限循环
  }

  componentWillUnmount() {
    // 清理工作
    // - 取消订阅
    // - 清除定时器
    // - 取消网络请求
  }
}

4.2 错误处理

jsx 复制代码
class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    // 更新状态,下次渲染时显示降级 UI
    return { hasError: true };
  }

  componentDidCatch(error, errorInfo) {
    // 记录错误信息
    console.error('Error:', error);
    console.error('Error Info:', errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

5. Hooks 时代的生命周期

5.1 使用 Hooks 模拟生命周期

jsx 复制代码
function HooksLifecycle() {
  // 模拟 constructor
  const [state, setState] = useState({
    count: 0
  });

  // 模拟 componentDidMount
  useEffect(() => {
    console.log('Component mounted');
    return () => {
      // 模拟 componentWillUnmount
      console.log('Component will unmount');
    };
  }, []);

  // 模拟 componentDidUpdate
  useEffect(() => {
    console.log('State updated:', state);
  }, [state]);

  return <div>{state.count}</div>;
}

5.2 生命周期方法与 Hooks 对照表

生命周期方法 Hooks 实现
constructor useState
componentDidMount useEffect(() => {}, \[\])
componentDidUpdate useEffect(() => {}, deps)
componentWillUnmount useEffect(() => { return () => {} }, \[\])
shouldComponentUpdate useMemo, useCallback
getDerivedStateFromProps useState + useEffect

6. 总结

6.1 生命周期选择建议

  1. 使用 componentDidMount 进行初始化操作
  2. 使用 componentDidUpdate 响应更新
  3. 使用 componentWillUnmount 清理资源
  4. 优先考虑新的生命周期方法
  5. 在合适的场景使用 Hooks

6.2 注意事项

  1. 避免在构造函数中执行副作用
  2. 不要在 getDerivedStateFromProps 中执行副作用
  3. 谨慎使用 shouldComponentUpdate
  4. 在 componentDidUpdate 中注意避免无限循环
  5. 确保在 componentWillUnmount 中清理所有副作用

6.3函数组件中的生命周期(通过 Hooks)

useEffect 可以模拟 componentDidMount 和 componentDidUpdate,通过将依赖项传递给 useEffect 来控制副作用的执行。传递空数组 \[\] 作为依赖项时,useEffect 只会在组件首次挂载时执行,相当于 componentDidMount。

清理副作用:useEffect 可以返回一个清理函数,相当于类组件中的 componentWillUnmount。

复制代码
import React, { useState, useEffect } from 'react';

const MyFunctionComponent = () => {
  const [counter, setCounter] = useState(0);

  // 模拟 componentDidMount 和 componentDidUpdate
  useEffect(() => {
    console.log('组件已挂载或更新');
    
    // 模拟 componentWillUnmount
    return () => {
      console.log('组件将卸载');
    };
  }, [counter]);  // 依赖项,只有当 counter 更新时,才会重新执行 useEffect

  return (
    <div>
      <p>Counter: {counter}</p>
      <button onClick={() => setCounter(counter + 1)}>增加计数</button>
    </div>
  );
};

export default MyFunctionComponent;

6.4 React 18 的新特性

React 18 引入了多个新特性,最显著的之一是 并发模式(Concurrent Mode),它改善了渲染性能,尤其是在 React 应用的响应式方面。虽然并发模式与生命周期方法的直接关系不大,但它会影响组件的挂载、更新和卸载过程。

React 18 还引入了 自动批量更新(Automatic Batching)和 Suspense 进一步增强了异步渲染的能力。这些新特性和生命周期方法并没有冲突,而是增强了现有的机制。

总结:

类组件:React 18 中,类组件的生命周期方法仍然有效,且与之前的版本一样。

函数组件:函数组件使用 useEffect 和其他 Hooks 来模拟传统的生命周期方法。

React 18 新特性:引入了并发模式、自动批量更新等,但它不会影响生命周期方法。

因此,如果你使用类组件,React 18 中依然可以使用传统的生命周期方法。如果使用函数组件,你可以通过 useEffect 来替代和模拟传统生命周期的行为。

相关推荐
子兮曰5 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰5 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万5 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝5 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋5 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁5 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95276 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大6 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师6 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学6 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端