当将空数组作为第二个参数传递时,React Hook useEffect缺少依赖。

3
我在我的函数组件中使用以下useEffect钩子,我只想使用一次(当组件挂载时)从API加载一些数据:
const Gear = () => {
  const [weaponOptions, setWeaponOptions] = useState([
    {
      key: "0",
      label: "",
      value: "null"
    }
  ]);
  const [weapon, setWeapon] = useState("null");

  useEffect(() => {
    console.log("Gear.tsx useEffect");

    const fetchWeaponsOptions = async (): Promise<void> => {
      const weaponsData = await getWeapons();
      const newWeaponOptions: DropdownOptionType[] = [
        ...weaponOptions,
        ...weaponsData.map(({ id, name }) => {
          return {
            key: id,
            label: name,
            value: id
          };
        })
      ];
      setWeaponOptions(newWeaponOptions);
    };

    fetchWeaponsOptions();
  }, []);

  // TODO add weapon dropdown on change, selected weapon state
  const handleWeaponChange = ({ value }: DropdownOptionType): void => {
    setWeapon(value);
  };

  return (
    <div>
      <h2>Gear:</h2>
      <Dropdown
        defaultValue={weapon}
        label="Weapon"
        name="weapon"
        options={weaponOptions}
        onChange={handleWeaponChange}
      />
    </div>
  );
};

React文档注释指出,当您仅希望在挂载和卸载时运行效果时,这是有效的做法:

如果您只想运行一次效果并清理它(在挂载和卸载时),可以将空数组([])作为第二个参数传递。这告诉React您的效果不依赖于任何来自props或state的值,因此它永远不需要重新运行。这不被视为特殊情况-它直接遵循依赖项数组的工作方式。

但是我收到了以下create-react-app linter警告:

35:6 warning React Hook useEffect has a missing dependency: 'weaponOptions'. Either include it or remove the dependency array react-hooks/exhaustive-deps

如果我将它作为依赖项传递,useEffect会在weaponOptions数组更改时触发,导致无限循环,因为hook本身会更改weaponOptions状态。如果我省略空数组参数,则会发生相同的事情。

这里的正确方法是什么?


请告诉我们 weaponOptions 是从哪里来的。 - Dennis Vash
@DennisVash 抱歉,为了让事情更清晰,我将整个功能组件代码添加到了初始问题中。 - Miha Šušteršič
2个回答

5

我只想在组件加载时使用一次,从API中加载一些数据。

因此,根据您的逻辑,您不需要依赖于组件的状态:

const INITIAL = [
  {
    key: '0',
    label: '',
    value: 'null'
  }
];

const App = () => {
  const [weaponOptions, setWeaponOptions] = useState(INITIAL);

  useEffect(() => {
    const fetchWeaponsOptions = async () => {
      const weaponsData = await getWeapons();
      const weaponsOptions = [
        ...INITIAL, // No state dependency needed
        ...weaponsData.map(({ id, name }) => {
          return {
            key: id,
            label: name,
            value: id
          };
        })
      ];
      setWeaponOptions(weaponsOptions);
    };

  }, []);

  return <></>;
};

但是,当你想要使用 useEffect 只执行一次,并且它依赖于一个状态时,通常会使用类似以下的布尔引用:

const App = () => {
  const [weaponOptions, setWeaponOptions] = useState(INITIAL);

  const isFirstFetch = useRef(true);

  useEffect(() => {
    const fetchWeaponsOptions = async () => {...}

    if (isFirstFetch.current) {
      fetchWeaponsOptions();
      isFirstFetch.current = false;
    }
  }, [weaponOptions]);

  return <></>;
};

1
这非常有道理 :). 非常感谢。 - Miha Šušteršič

0

正如您所看到的,这不是一个错误而是一个警告。React 告诉您在 useEffect 中使用了 weaponOptions,但您没有将其作为依赖项传递。再次强调,这只是一个警告,您不必这样做。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接