React 中 props 更改时更新状态

要在 React 中的 props 更改时更新状态:

  1. 将 props 作为依赖项传递给 useEffect 钩子。
  2. 每次 props 改变时,useEffect 中的逻辑都会重新运行。
import {useEffect, useState} from 'react';

function Child({parentCount}) {
  const [childCount, setChildCount] = useState(0);

  useEffect(() => {
    setChildCount(parentCount * 2);

    console.log('useEffect logic ran');
  }, [parentCount]); // 👈️ 将 props 添加为依赖项

  return (
    <div>
      <button>Child count {childCount}</button>
    </div>
  );
}

export default function Parent() {
  const [parentCount, setParentCount] = useState(0);

  return (
    <div>
      <button onClick={() => setParentCount(current => current + 1)}>
        Parent count: {parentCount}
      </button>

      <hr />

      <Child parentCount={parentCount} />
    </div>
  );
}

我们使用 useEffect 钩子在组件的 props 更改时更新组件的状态。

useEffect(() => {
  setChildCount(parentCount * 2);

  console.log('useEffect logic ran');
}, [parentCount]); // 👈️ 将 props 添加为依赖项

每次其依赖项之一发生更改时,都会重新运行 useEffect 钩子中的逻辑。

每次 parentCount 属性改变时,useEffect 钩子都会重新运行,我们使用 setChildCount 函数来更新状态。

如果不想在初始渲染时运行 useEffect 钩子中的逻辑,但仅在特定属性更改时,请使用 ref 在初始渲染时提前返回。

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

function Child({parentCount}) {
  const [childCount, setChildCount] = useState(0);

  const isFirstRender = useRef(true);

  useEffect(() => {
    if (isFirstRender.current) {
      isFirstRender.current = false;
      return; // 👈️ 如果第一次渲染,请提前返回
    }
    setChildCount(parentCount * 2);

    console.log('useEffect logic ran');
  }, [parentCount]);

  return (
    <div>
      <button>Child count {childCount}</button>
    </div>
  );
}

export default function Parent() {
  const [parentCount, setParentCount] = useState(0);

  return (
    <div>
      <button onClick={() => setParentCount(current => current + 1)}>
        Parent count: {parentCount}
      </button>

      <hr />

      <Child parentCount={parentCount} />
    </div>
  );
}

当在 mount 上运行 useEffect 钩子时,我们使用 ref 提前退出。

如果我们想监听属性更改但需要跳过第一次渲染,请使用此方法。

需要注意的是,如果我们更新一个 prop 的值并且这个 prop 存在于 hook 的依赖数组中,你会导致一个无限的重新渲染循环。

下面是一个演示此问题的示例。

import {useEffect, useState} from 'react';

function Child({parentCount, setParentCount}) {
  useEffect(() => {
    // 👇️ 这里将导致无限循环
    setParentCount(current => current + 1);

    console.log('useEffect logic ran');
  }, [parentCount, setParentCount]); // 👈️ parentCount 是一个依赖项

  return (
    <div>
      <button>Parent count {parentCount}</button>
    </div>
  );
}

export default function Parent() {
  const [parentCount, setParentCount] = useState(0);

  return (
    <div>
      <Child setParentCount={setParentCount} parentCount={parentCount} />
    </div>
  );
}

问题是我们将 parentCount 属性添加到了钩子的依赖数组中,但我们也在钩子中更新了它的值。

每次运行 useEffect 时, parentCount 的值都会改变,这会再次重新运行钩子,因为 parentCount 在其依赖项数组中。