使用两个不同的 useState 钩子来存储数组和过滤数组以传递给组件是否正确?

Is it correct to use two different useState hook to store an array and a filtered array for passing to components?

我目前正在将我的 Pokemon 项目移植到 React,因为几周前我刚刚学习了 React 的基础知识,想看看我能适应它的程度。现在,我构建代码的方式是使用两个不同的 useState 挂钩,一个用于从 PokeAPI 获取的原始数组,并且只设置一次。这个原始数组也被传递到我的表单 object 中,它根据一些表单元素(如 Pokemon 类型或 Pokemon 的名称)对其进行过滤。另一个 useState 挂钩用于跟踪 filteredList,它是使用 Array.map() 函数呈现给网站的内容。

  const [pokemonList, setPokemonList] = useState([]);
  const [filteredList, setPokemonFilteredList] = useState([]);

这是我们获取和设置状态的 useEffect() 挂钩

  useEffect(() => {
    const getPokemon = async () => {
      const list = await fetchPokemon();
      setPokemonList(list);
      setPokemonFilteredList(list);
    };

最后 pokemonList 状态变量和 setPokemonFilteredList 方法被传递到 <PokemonSearchForm>

<PokemonSearchForm pokemonList={pokemonList} setPokemonList={setPokemonFilteredList} />

那么正如我的问题标题所暗示的那样,我使用两种不同的方式 useState() 'correct' 吗?也许 child 组件访问 pokemonList 变量的另一种方式?但我相信这可能是一个 anti-pattern.

我会用几种不同的方式重构它:

  1. 在父组件中保留过滤器状态,子组件会在这些更改时简单地通知它。
  2. useMemo 放弃 useState,后者每次依赖项发生变化时都会计算一个值。
function YourComponent() {
  const [filters, setFilters] = useState({});
  const [pokemons, setPokemons] = useState([]);

  useEffect(
    () => {
      const list = await fetchPokemon();
      setPokemons(list);
    },
    []
  );

  // This will run each time `filters` or `pokemons` change.
  const filteredPokemons = useMemo(
    () => {
      return pokemons.filter((pokemon) => {
        // Perform any filtering logic you may have,
        // based on the filters set by the child component.

        if (filters.name) {
          return pokemon.name.includes(filters.name);
        }

        // etc...
      });
    },
    [filters, pokemons]
  );

  return (
    <PokemonSearchForm
      pokemons={filteredPokemons}
      onChange={setFilters}
    />
  );
}

更好的做法是保持重复状态,或者在这种情况下,派生状态,因为您可能运行进入分歧。例如,如果您的原始 pokemon 数据得到更新,您将如何确保过滤后的数据得到更新,然后重新应用过滤器?长毛的速度非常快。

首选的替代方法是在状态中维护原始数据和过滤器,然后在渲染期间计算派生状态(在这种情况下,过滤列表)。

function App() {
  const [pokemonList, setPokemonList] = useState([]);
  // Some default filter state
  const [filters, setFilters] = useState({
    types: ["any"],
    search: ""
  });

  const filteredList = pokemonList.filter((pokemon) => {
    // Filter logic here
  });

  return <PokemonSearchForm setFilters={setFilters} />
}