React 组件生命周期

每个 React 组件都有一个“生命周期”,你可以把它想象成组件从诞生到消亡所经历的各个阶段。理解生命周期可以让你在组件的不同阶段执行代码,例如在组件首次渲染后获取数据,或在组件被移除前进行清理。

组件的生命周期主要分为三个阶段:

  1. 挂载 (Mounting): 组件实例被创建并插入到 DOM 中。
  2. 更新 (Updating): 组件的 props 或 state 发生变化,导致重新渲染。
  3. 卸载 (Unmounting): 组件从 DOM 中被移除。

在函数组件中,我们主要使用 useEffect Hook 来处理这些生命周期事件。

使用 useEffect Hook 管理生命周期

useEffect Hook 是一个功能强大的 API,它统一了处理组件挂载、更新和卸载时副作用(side effects)的方式。

1. 挂载:componentDidMount

如果你想在组件首次渲染到屏幕后执行一些代码(例如,从服务器获取数据),你可以使用 useEffect,并提供一个空的依赖数组 []

import React, { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // 这个函数会在组件首次渲染后执行
    console.log('组件已挂载');
    fetch(`https://api.example.com/users/${userId}`)
      .then(response => response.json())
      .then(data => setUser(data));

  }, []); // 空数组告诉 React 这个 effect 只需运行一次

  if (!user) {
    return <div>Loading...</div>;
  }

  return <div>{user.name}</div>;
}

2. 更新:componentDidUpdate

当你希望在组件的 props 或 state 更新后执行副作用时,你可以在 useEffect 的依赖数组中指定这些 props 或 state。

在下面的例子中,每当 userId 这个 prop 发生变化时,组件都会重新获取新的用户数据。

import React, { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // 这个 effect 会在首次渲染后运行,
    // 并且在每次 userId 更新后也会运行。
    console.log(`获取 userId 为 ${userId} 的数据`);
    fetch(`https://api.example.com/users/${userId}`)
      .then(response => response.json())
      .then(data => setUser(data));

  }, [userId]); // 依赖数组中包含 userId

  // ...
}

如果不提供依赖数组,effect 将在每次组件渲染后都会执行,这通常不是我们想要的,因为它可能导致不必要的性能开销。

3. 卸载:componentWillUnmount

有些副作用需要清理,比如定时器或事件监听器。useEffect 允许你返回一个清理函数。这个函数会在组件从 DOM 中移除前执行,也会在下一次 effect 运行前执行。

让我们看一个订阅聊天室状态的例子:

import React, { useState, useEffect } from 'react';

function ChatRoom({ roomId }) {
  const [status, setStatus] = useState('offline');

  useEffect(() => {
    console.log(`订阅到房间 ${roomId}`);
    ChatAPI.subscribeToRoomStatus(roomId, (newStatus) => {
      setStatus(newStatus);
    });

    // 返回一个清理函数
    return () => {
      console.log(`从房间 ${roomId} 取消订阅`);
      ChatAPI.unsubscribeFromRoomStatus(roomId);
    };
  }, [roomId]); // 当 roomId 改变时,会先取消旧的订阅,再设置新的订阅

  return <div>Room status: {status}</div>;
}

总结

useEffect 为函数组件提供了一个统一的、强大的方式来处理生命周期中的各种副作用:

生命周期事件useEffect 用法
挂载useEffect(() => { ... }, [])
更新useEffect(() => { ... }, [dep1, dep2])
卸载useEffect(() => { return () => { ... } }, [])

通过 useEffect,我们可以将相关的逻辑(例如,订阅和取消订阅)组织在一起,使得代码更加清晰和易于维护。接下来,我们将深入了解 React 组件的 API。