在循环内调用 React hook 的规范替代方案是什么?

Ada*_*ner 3 reactjs react-hooks

我知道不应该在循环内调用 React hooks。然而,我不确定规范的替代方案是什么(如果有的话)。

作为示例,请考虑以下内容 ( CodeSandbox ):

import { useState, useEffect } from "react";

export default () => {
  const userIds = [1, 2, 3];
  const users = [];

  for (let userId of userIds) {
    const user = useFetchUser(userId);

    users.push(user);
  }

  return (
    <div>
      <h1>Users</h1>
      {users.map((user) => (
        <div>
          {user.id} - {user.name}
        </div>
      ))}
    </div>
  );
};

const useFetchUser = (id) => {
  const nameMap = {
    1: "Alice",
    2: "Bob",
    3: "Carol",
  };
  const [user, setUser] = useState({});

  useEffect(() => {
    setTimeout(() => {
      const name = nameMap[id];

      setUser({
        id,
        name,
      });
    }, 1000);
  }, [id]);

  return user;
};
Run Code Online (Sandbox Code Playgroud)

这是不可取的(但确实有效useFetchUser),因为它涉及在循环内部调用钩子( )。处理这种情况的规范方法是什么?


我想到的第一个想法是使用一个useFetchUsers(复数)钩子来获取所有用户而不是仅一个用户。然而,创建这样一个useFetchUser像这样使用的钩子是行不通的。

const useFetchUsers = (ids) => {
  const users = [];

  for (let id of ids) {
    const user = useFetchUser(id);

    users.push(user);
  }

  return users;
};
Run Code Online (Sandbox Code Playgroud)

或者至少它仍然给出以下 lint 错误:

React Hook "useFetchUser" may be executed more than once. Possibly because it is called in a loop. React Hooks must be called in the exact same order in every component render. (react-hooks/rules-of-hooks)eslint
Run Code Online (Sandbox Code Playgroud)

因此,也许解决方案是放弃useFetchUser并使用新逻辑编写自己的useFetchUsers钩子来替换useFetchUser. 在这个简单的例子中,这可能没问题。然而,在各种更复杂的现实世界场景中,放弃任何等效的东西通常是不切实际的useFetchUser

Mat*_*bst 6

您需要更改组成组件的方式。制作一个User调用钩子的组件:

const App = () => {
  const userIds = [1, 2, 3];

  return (
    <div>
      <h1>Users</h1>
      {userIds.map((userId) => <User userId={userId} key={userId} />)}
    </div>
  );
}

const User = (props) => {
  const userId = props.userId;
  // Returns `undefined` until the user is loaded
  const user = useFetchUser(userId);

  if (!user) {
    return null;
  }

  return (
    <div>{user.name}</div>
  );
}
Run Code Online (Sandbox Code Playgroud)

  • @AdamZerner 在循环中正确调用钩子的唯一方法是渲染一堆单独的组件,每个组件都调用钩子。因此,要么遵循该模式,要么修改您的钩子逻辑。恕我直言,好的做法是尽可能将业务逻辑保留在 React(或您使用的任何渲染框架)之外。将帮助您编写更多可维护和无状态/最小状态组件。 (4认同)
  • 如果我想将数据缓存到父元素中怎么办?或者这只是一个坏主意? (2认同)