React18 生命周期


在这里插入图片描述

注:只有类组件中才有传统的生命周期钩子


🔥生命周期顺序

阶段生命周期方法说明是否常用
挂载constructor初始化 state,绑定方法等✅ 一般必要
getDerivedStateFromProps派生状态,极少需要❌ 不推荐
render渲染 JSX✅ 必须
componentDidMount组件挂载后执行,适合数据请求、副作用处理✅ 非常常用
更新getDerivedStateFromProps同上❌ 很少用
shouldComponentUpdate控制是否 re-render,提高性能✅ 有优化需求时用
render再次渲染
getSnapshotBeforeUpdate获取更新前 DOM 状态,如滚动位置⚠️ 偶尔用
componentDidUpdate更新后执行副作用、DOM 操作等✅ 很常用
卸载componentWillUnmount组件卸载前执行,清理副作用✅ 非常常用
错误componentDidCatch捕获子组件错误✅ 有一定复杂度时用
getDerivedStateFromError配合上面处理错误 UI⚠️ 一般用得少

一、挂载阶段(初始化)

1. constructor(props)

类中的构造函数

  • 在 React 组件挂载之前被调用。
  • 初始化函数内部 state 或者在 this 上挂载方法。
  • 如果使用了 constructor,则必须在其中书写 super(props),并将传给 constructorprops 传入 super 内,否则会导致指向错误(ES6语法)。

.

2. static getDerivedStateFromProps(newprops, state)

是 React 类组件中的一个静态方法,接收新的props和先前的state,根据props的变化来更新state。

可以在组件接收新的props时自动更新state,而无需手动进行状态管理。state 的值在任何时候都取决于 props

React官方文档建议避免在组件中使用 static getDerivedStateFromProps(),会导致代码冗余,使组件难以维护

  • 不能访问到组件实例 this,指向 undefined

  • 在创建或更新阶段调用,或者在 propsstaterender 方法前调用。

  • 根据新的 props 和当前的 state 来更新 state。它通常用于处理 props 变化时 state 的更新逻辑。如果返回 null 则不更新任何内容。

返回值:

  • 必须要有返回值,要么返回新的 state 对象,要么返回null
  • 如果返回一个新的 state 对象,这个新的 state 将会与当前的 state 合并。
  • 如果返回 null 来表示不更新任何 state

使用场景:

  • 当 props 改变时,需要更新 state:例如,你可能有一个受控组件,其内部状态需要基于传入的 props 来更新。
  • 在挂载时,根据初始 props 来设置 state:虽然可以在构造函数中设置初始 state,但有时你可能想基于传入的 props 来设置初始 state。
class MyComponent extends React.Component {  
  constructor(props) {  
    super(props);  
    // 初始化 state,使用传入的 msg 作为初始值  
    this.state = { msg: props.initialMsg };  
  }  
  
  static getDerivedStateFromProps(props, state) {  
    // 如果传入的 msg 与当前 state 中的 msg 不同,则更新 state  
    if (props.msg !== state.msg) {  
      return { msg: props.msg };  
    }  
    // 如果传入的 msg 没有变化,则返回 null 表示不更新 state  
    return null;  
  }  
  
  render() {  
    // 在 render 方法中使用 state 中的 msg  
    return <div>{this.state.msg}</div>;  
  }  
}

用来替代 componentWillMount() 在组件即将被挂载到页面时执行(16.3废弃告警)

.

3. render()

渲染组件,用于返回组件的 JSX。

React最重要的步骤,创建虚拟DOM,进行diff算法,更新DOM树都在此进行。此时就不能更改state了。

不要在 render() 里面使用 setState,否则会触发死循环导致内存崩溃。

.

4. componentDidMount()

在组件被挂载到页面后立即执行,只调用一次

可以执行任何依赖于DOM的初始化操作,如数据请求、DOM 操作等


二、更新阶段

1. static getDerivedStateFromProps(newprops, state)

与挂载阶段相同,接收新的props和先前的state,根据props的变化来更新state

.

2. shouldComponentUpdate(nextProps, nextState)

  • 在组件更新之前调用,并返回一个布尔值来控制组件是否进行更新:

    • 返回 true 时组件更新,会继续其渲染流程。

    • 返回 false 则不更新,会跳过渲染,组件将保持原样,后面所有的生命周期也都不会执行

    • 如果没有这个函数,则默认返回 true

  • 使用 this.forceUpdate() 强制刷新 会跳过 shouldComponentUpdate()生命周期方法,谨慎使用

  • 不要在 shouldComponentUpdate() 中调用 setState(),否则会导致无限循环调用更新、渲染,直至浏览器内存崩溃。

参数:

  • nextProps:即将接收的新 props。
  • nextState:即将更新的 state。

使用场景:
主要用于性能优化。
当你知道在某些情况下组件的输出不会因 props 或 state 的变化而改变时,你可以通过返回 false 来阻止不必要的渲染。

.

3. render()

如果 shouldComponentUpdate 返回 true,或者没有定义 shouldComponentUpdate 这个方法,React将重新渲染组件

.

4. getSnapshotBeforeUpdate(prevProps, prevState)

在最近一次渲染输出(提交到DOM节点)之前调用,即在 DOM 更新之前被调用,可以在这里获取更新前的状态,用于捕获 DOM 更新前的某些信息(如滚动位置)。

在更新之前获取快照,此用法并不常见

返回值:

  • 必须要有返回值,要么返回 快照值(snapshot),要么返回null
  • 快照值(snapshot)可以是任何类型的值
  • 如果返回 null ,则意味着你没有为该组件的更新过程提供任何特定的快照值
  • 返回的值(无论是快照值还是 null)都会作为第三个参数传递给 componentDidUpdate(prevProps, prevState, snapshot) 方法

.

5. componentDidUpdate(prevProps, prevState, snapshot)

  • 在真实 DOM 更新完成后被调用

  • 可以在这里执行依赖于 DOM 更新的操作,如数据请求、DOM 操作等。

  • 但不能在此方法中直接调用 `setState ,因为这可能会导致额外的更新循环。


三、卸载阶段(销毁)

1. componentWillUnmount()

  • 在组件即将被卸载及销毁之前直接调用

  • 通常在这里执行必要的清理操作,例如,清除定时器,取消网络请求或删除在 componentDidMount 中创建的DOM元素。

注:除了 render() ,其他所有的生命周期函数都可以没有



⚡️ 执行顺序

在父子组件中生命周期函数执行顺序:

  • 挂载阶段:
    1. 父组件:rconstructor()
    2. 父组件:static getDerivedStateFromProps()
    3. 父组件:render()
    4. 子组件:rconstructor()
    5. 子组件:static getDerivedStateFromProps()
    6. 子组件:render()
    7. 子组件:componentDidMount()
    8. 父组件:componentDidMount()

.

  • 更新阶段:
    1. 父组件:static getDerivedStateFromProps()
    2. 父组件:shouldComponentUpdate()
    3. 父组件:render()
    4. 父组件:getSnapshotBeforeUpdate()
    5. 父组件:componentDidUpdate()
    6. 子组件:static getDerivedStateFromProps()
    7. 子组件:shouldComponentUpdate()
    8. 子组件:render()
    9. 子组件:getSnapshotBeforeUpdate()
    10. 子组件:componentDidUpdate()

.

  • 卸载阶段:
    1. 子组件:componentWillUnmount()
    2. 父组件:componentWillUnmount()

🚀函数组件中常用生命周期 Hook 对应

函数组件 Hook等价类组件生命周期说明是否常用
useEffect(() => {}, [])componentDidMount初次挂载副作用✅ 必用
useEffect(() => {...}, [deps])componentDidUpdate依赖变化时触发副作用✅ 必用
useEffect(() => {... return ...})componentWillUnmount卸载清理逻辑✅ 必用
useLayoutEffect类似 componentDidMount/Update但在 DOM 更新前同步执行⚠️ 特殊场景用
useRef存储跨 render 的状态/DOM 引用非生命周期,但常用于关联✅ 常用
useMemo / useCallbackshouldComponentUpdate优化提升性能,避免不必要 render✅ 优化时用
useErrorBoundary(第三方)componentDidCatch错误边界⚠️ 错误处理时用

React 常用 Hook 生命周期行为实战模板

import React, { useEffect, useRef, useState, useMemo, useCallback } from 'react';

// 模拟数据请求
const fetchData = async () => {
  return new Promise((resolve) => {
    setTimeout(() => resolve('数据加载完成 ✅'), 1000);
  });
};

const MyComponent = () => {
  // 1️⃣ useState:组件状态
  const [count, setCount] = useState(0);
  const [data, setData] = useState('');

  // 2️⃣ useRef:保存 DOM 或跨 render 不变的值
  const domRef = useRef<HTMLDivElement>(null);
  const isFirstRender = useRef(true); // 用于跳过首次 useEffect

  // 3️⃣ useEffect:模拟 componentDidMount,初次加载请求数据
  useEffect(() => {
    fetchData().then((res) => {
      setData(String(res));
    });

    // 组件卸载时清理定时器(模拟 componentWillUnmount)
    return () => {
      console.log('✅ 组件卸载,执行清理');
    };
  }, []); // 空依赖数组 => 只在挂载时运行一次

  // 4️⃣ useEffect:模拟 componentDidUpdate,但跳过首次
  useEffect(() => {
    if (isFirstRender.current) {
      isFirstRender.current = false;
      return;
    }
    console.log('🔁 count 更新了:', count);
  }, [count]);

  // 5️⃣ useMemo:缓存计算值(避免不必要的计算)
  const doubleCount = useMemo(() => {
    console.log('🧠 重新计算 doubleCount');
    return count * 2;
  }, [count]);

  // 6️⃣ useCallback:缓存函数(避免子组件重复渲染)
  const handleAdd = useCallback(() => {
    setCount((prev) => prev + 1);
  }, []);

  return (
    <div ref={domRef}>
      <h2>🌟 React 生命周期 Hook 示例</h2>
      <p>数据:{data || '加载中...'}</p>
      <p>count:{count}</p>
      <p>doubleCount(缓存):{doubleCount}</p>
      <button onClick={handleAdd}>点击 +1</button>
    </div>
  );
};

export default MyComponent;

版本迁移

React 16.3 版本

  • componentWillMountcomponentWillReceivePropscomponentWillUpdate 启用弃用告警,并建议开发者避免使用它们。

  • 这三个生命周期因为经常会被误解和滥用,所以被称为 “不安全”(unsafe)的生命周期unsafe 不是指安全性,而是表示使用这些生命周期的代码,有可能在未来的 React 版本中存在缺陷,可能会影响未来的异步渲染)

  • React提供了一个临时的“escape hatch”,允许开发者继续使用这些“不安全”的方法,但需要在它们前面加上UNSAFE_前缀
    如:UNSAFE_componentWillMountUNSAFE_componentWillReceivePropsUNSAFE_componentWillUpdate

  • 新增生命周期 getDerivedStateFromProps(nextProps, prevState)getSnapshotBeforeUpdate(prevProps, prevState)(用的很少)

.

React 17.0 版本

  • componentWillMountcomponentWillReceivePropscomponentWillUpdate 仍然可以在 React 17 中使用,但React会给出警告。

  • 使用 UNSAFE_componentWillMountUNSAFE_componentWillReceivePropsUNSAFE_componentWillUpdate ,则不会警告。

  • 其余的生命周期方法在React 17中仍然存在,但建议使用Hooks(如useEffect)进行替代,以实现更简洁和可维护的代码。

.

React 18.0 版本

React 18.0 版本在生命周期方法上与 React 17.0 版本类似,并没有引入新的改动。

但引入了新的特性和优化,特别是与并发模式(Concurrent Mode)和异步渲染相关的部分。

  • 并发模式:
    React 18引入了并发模式,允许React在渲染过程中暂停、恢复和优先处理重要的更新。这可能会影响到某些生命周期方法的执行顺序和频率。

  • 异步渲染:

  • startTransition(): 用于告诉React哪些组件的渲染可以被推迟执行。

  • useDeferredValue(): 用于缓存某些组件的状态,以便推迟渲染。

  • useTransition(): 用于告诉React哪些状态更新应该被推迟执行,并返回一个Promise,表示更新完成后的状态。


总结

常用的钩子:

  • render():初始化渲染或更新渲染调用
  • componentDidMount():开启监听,发送ajax请求
  • componentWillUnmount():做些收尾工作,如清理定时器

.

即将废弃的钩子:

  • componentWillMount()
  • componentWillReceiveProps()
  • componentWillUpdate()

现在使用会出现警告,下个版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用

.

新增的钩子:

  • static getDerivedStateFromProps()
  • getSnapshotBeforeUpdate()

使用场景都比较少


函数式组件 useEffect Hook

函数式组件没有内置的生命周期钩子,但可以使用 useEffect Hook 来替代传统生命周期钩子的功能。

Hooks提供了更灵活、更简洁的方式来处理组件中的状态、副作用和逻辑,是React现代开发的重要组成部分。

useEffect 接收两个参数:

  • 第一个参数(回调函数):这是一个函数,它会在组件挂载后执行,并在每次更新后重新执行(除非指定了依赖项数组)。

    1. 在组件挂载后执行,并在每次更新后重新执行:

      useEffect(() => {  
        console.log('Component mounted or updated');  
      }); 
      
    2. 若返回一个函数,则该函数会在组件卸载时执行。相当于 componentWillUnmount()

      useEffect(() => {  
        const timer = setInterval(() => {  
          console.log('Timer is running');  
        }, 1000);  
      
        // 返回一个清理函数,在组件卸载时执行  
        return () => {  
          clearInterval(timer);  
          console.log('组件卸载前清空定时器');  
        };  
      },[]); // 由于没有依赖项,这个 effect 只在组件首次渲染时执行 
      
  • 第二个参数(依赖项数组):这是一个可选的数组,它包含了你的 effect 依赖的变量。

    1. 当数组中的某个值发生变化时,effect 才会重新执行

      只要 count 变化时,effect 回调会执行。相当于 componentDidMount() + componentDidUpdate(只监听 count)

      useEffect(() => {  
        console.log('Component mounted or updated');  
      }, [count]); 
      
    2. 如果不指定这个数组,那么 effect 会在每次组件渲染后都执行。

      任何 state 发生变化,effect 回调都会执行。相当于 componentDidMount() + componentDidUpdate(监听所有 state)

      useEffect(() => {  
        console.log('Component mounted or updated');  
      }); 
      
    3. 如果指定一个空数组 [],则 effect 只会在组件首次渲染时执行。

      只会在组件首次渲染时执行,相当于 componentDidMount()(不监听任何 state)

      useEffect(() => {  
        console.log('Component mounted or updated');  
      },[]); 
      

综上:useEffect Hook 可以看做是 componentDidMount()componentDidUpdatecomponentWillUnmount() 这三个生命周期钩子的组合。

useEffect 的强大之处在于它可以精细地控制副作用的触发时机通过指定不同的依赖项数组来实现。这使得它在处理复杂的副作用逻辑时更加灵活和强大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猫老板的豆

你的鼓励将是我创作的最大动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值