【问题标题】:Infinite loop with useEffect hook with useSelector带有 useEffect 钩子和 useSelector 的无限循环
【发布时间】:2021-12-09 14:48:09
【问题描述】:

我的 React Native 应用程序中有一个标准化的 Redux 存储。

我的reducer的结构是:

{
  byId: {},
  allIds: []
}

在我的组件中,我使用 useSelector 钩子获取 Redux 状态片段:

const categories = useSelector((state: AppState) =>
  state.products.allIds.map((id) => state.categories.byId[id.toString()])
);

useSelector 中的逻辑只是将byId 对象转换为数组。

当我将categories 数组设置为依赖项时发生无限循环:

const [values, setValues] = useState<any[]>([]);

useEffect(() => {
  setValues([{ id: 1 }]);
  console.log("logging");
}, [categories]);

不确定是什么问题。我相信是 useSelector 逻辑将对象转换为数组。

编辑:

完整的组件代码:

// React
import React, { useEffect, useState } from "react";

// React redux
import { useSelector } from "react-redux";
import { AppState } from "@reducers/rootReducer";

// Logic
import ProductsScreenLogic from "./ProductsScreen.logic";

// Common components
import ScreenView from "@common/screen/Screen.view";

// Components
import NewProductModalView from "@components/products/new-product-modal/NewProductModal.view";
import ProductsTabsView from "@components/products/products-tabs/ProductsTabs.view";
import ProductListView from "@components/products/products-list/ProductList.view";
import CategoryListView from "@components/products/category-list/CategoryList.view";

const ProductsScreenView: React.FC = () => {
  const { displayProductList, setDisplayProductList, products } =
    ProductsScreenLogic();


  // Makes the categories ById object into an array of categories
  const categories = useSelector((state: AppState) => state.categories.allIds.map((id) => state.categories.byId[id.toString()])
  );


  const [values, setValues] = useState<any[]>([]);

  useEffect(() => {
    setValues([{ id: 1 }]);
    console.log("logging");
  }, [categories]);

  return (
    <>
      <NewProductModalView />
      <ScreenView></ScreenView>
    </>
  );
};

export default ProductsScreenView;

【问题讨论】:

  • 没有发现上述代码有任何问题,请发布完整的组件代码
  • @sojin 更新了帖子

标签: react-native redux react-redux infinite-loop normalizr


【解决方案1】:

在 useEffect 中,您更新状态,从而导致渲染,渲染调用 useSelector 每次都为 useEffect 返回新数组,从而导致更新状态。要修复,您可以从 useEffect 依赖数组中删除类别

【讨论】:

    【解决方案2】:

    问题是您的选择器总是返回一个新的引用(因为调用了map)。你可以改用createSelector,它会记住它,并且只在allIdsbyId 中的某些内容发生变化时返回一个新的引用:

    const selectAllCategories = createSelector(
        (state: AppState) => state.categories.allIds,
        (state: AppState) => state.categories.byId,
        (categoriesIds, categoriesById) => categoriesIds.map((id) => categoriesById[id.toString()])
    );
    

    但理想情况下,您应该避免使用贯穿整个 byId 对象的选择器,因为它有点否定了具有标准化状态的好处。你应该有一个只选择state.categories.allIds的父组件,然后将ids作为props传递给子组件,每个子组件都会选择自己的state.categories.byId[id]。这样,如果类别发生变化,只会重新渲染相应的子组件,而不是让父组件和所有子组件重新渲染。

    【讨论】:

      猜你喜欢
      • 2020-10-16
      • 1970-01-01
      • 2021-04-10
      • 2021-03-07
      • 1970-01-01
      • 2021-04-29
      • 2021-08-28
      • 2019-10-02
      • 2021-09-30
      相关资源
      最近更新 更多