[英]Infinite loop with useEffect hook with useSelector
I have a normalized Redux store in my React Native application.我的 React Native 应用程序中有一个标准化的 Redux 存储。
The structure of my reducer is:我的减速机的结构是:
{
byId: {},
allIds: []
}
In my component, I get the slice of Redux state using the useSelector
hook:在我的组件中,我使用useSelector
钩子获取 Redux 状态的useSelector
:
const categories = useSelector((state: AppState) =>
state.products.allIds.map((id) => state.categories.byId[id.toString()])
);
The logic in the useSelector
just converts the byId
object into an array. useSelector
的逻辑只是将byId
对象转换为数组。
The infinite looping occurs when I set the categories
array as a dependency:当我将categories
数组设置为依赖项时,会发生无限循环:
const [values, setValues] = useState<any[]>([]);
useEffect(() => {
setValues([{ id: 1 }]);
console.log("logging");
}, [categories]);
Not sure what is the problem.不知道是什么问题。 I believe it's the useSelector
logic that converts the objects into an array.我相信是useSelector
逻辑将对象转换为数组。
EDIT:编辑:
Full component code:完整的组件代码:
// 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;
In useEffect you update the state , witch cause render , render call useSelector witch every time return new array for useEffect , witch cause update the state.在 useEffect 中,您更新状态,导致渲染,每次渲染调用 useSelector 为 useEffect 返回新数组,导致更新状态。 To fix you can remove categories from useEffect dependency array要修复您可以从 useEffect 依赖项数组中删除类别
The problem is that your selector always returns a new reference (because of the call to map
).问题是你的选择器总是返回一个新的引用(因为调用了map
)。 You could instead use createSelector
which will memoize it and only return a new reference when something inside either allIds
or byId
changes:您可以改为使用createSelector
,它会记住它,并且仅在allIds
或byId
发生更改时才返回新引用:
const selectAllCategories = createSelector(
(state: AppState) => state.categories.allIds,
(state: AppState) => state.categories.byId,
(categoriesIds, categoriesById) => categoriesIds.map((id) => categoriesById[id.toString()])
);
But ideally you should avoid such selectors that go through the whole byId
object, because it kind of negates the benefits of having a normalized state.但理想情况下,您应该避免使用遍历整个byId
对象的此类选择器,因为它在byId
否定了具有规范化状态的好处。 You should rather have a parent component that only selects state.categories.allIds
, and then passes the ids as props to child components, and every child component will select its own state.categories.byId[id]
.你应该有一个只选择state.categories.allIds
的父组件,然后将 ids 作为道具传递给子组件,每个子组件都会选择自己的state.categories.byId[id]
。 This way if a category changes, only the corresponding child component will rerender, instead of having the parent and all of the children rerender.这样,如果类别发生变化,则只有相应的子组件会重新渲染,而不是让父组件和所有子组件重新渲染。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.