英文:
custom hook create infinite loop
问题
这个钩子创建了一个无限循环。我不明白为什么,因为我的依赖数组已经设置好了。
错误信息:Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
自定义钩子:
export const useListGuessers = () => {
const [list, setList] = useState([]);
const hasMarketing = UserHelper.hasAuthorization(AUTHORIZATION_MARKETING);
const hasTechnical = UserHelper.hasAuthorization(AUTHORIZATION_TECHNICAL);
const dashboardGroups = new DashboardGroups({hasMarketing, hasTechnical});
const guessers = [
...dashboardGroups.appProductGroup(),
...dashboardGroups.articlesGroup(),
...dashboardGroups.mediasGroup(),
...dashboardGroups.productsGroup(),
...dashboardGroups.orderableProductsGroup(),
...dashboardGroups.typesGroup(),
...dashboardGroups.usersGroup(),
...dashboardGroups.othersGroup(),
...dashboardGroups.userManagementGroup(),
];
const filteredGuesser = guessers
.filter(({canShow}) => canShow)
.map((guesser) => {
return {
label: guesser.label ?? guesser.value.options.label,
link: (guesser.operation && `user-management/${guesser.operation}`) ?? guesser.value.name,
};
})
.sort((a, b) => a.label.localeCompare(b.label));
useEffect(() => {
filteredGuesser && setList(filteredGuesser);
}, [filteredGuesser]);
return list;
};
这个类:
export class DashboardGroups {
authorizations: {hasMarketing: boolean; hasTechnical: boolean};
constructor(authorizations: {hasMarketing: boolean; hasTechnical: boolean}) {
this.authorizations = authorizations;
}
// [所有的分组在这里...]
getGroups = () => {
// 返回每个分组的标签和作为“children”的对象
};
}
英文:
This hook create an inifite loop. I don't understnd why, since my dependencies array is set.
Error : Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
Custom hook :
export const useListGuessers = () => {
const [list, setList] = useState([]);
const hasMarketing = UserHelper.hasAuthorization(AUTHORIZATION_MARKETING);
const hasTechnical = UserHelper.hasAuthorization(AUTHORIZATION_TECHNICAL);
const dashboardGroups = new DashboardGroups({hasMarketing, hasTechnical});
const guessers = [
...dashboardGroups.appProductGroup(),
...dashboardGroups.articlesGroup(),
...dashboardGroups.mediasGroup(),
...dashboardGroups.productsGroup(),
...dashboardGroups.orderableProductsGroup(),
...dashboardGroups.typesGroup(),
...dashboardGroups.usersGroup(),
...dashboardGroups.othersGroup(),
...dashboardGroups.userManagementGroup(),
];
const filteredGuesser = guessers
.filter(({canShow}) => canShow)
.map((guesser: any) => {
return {
label: guesser.label ?? guesser.value.options.label,
link: (guesser.operation && `user-management/${guesser.operation}`) ?? guesser.value.name,
};
})
.sort((a, b) => a.label.localeCompare(b.label));
useEffect(() => {
filteredGuesser && setList(filteredGuesser);
}, [filteredGuesser]);
return list;
};
The class :
export class DashboardGroups {
authorizations: {hasMarketing: boolean; hasTechnical: boolean};
constructor(authorizations: {hasMarketing: boolean; hasTechnical: boolean}) {
this.authorizations = authorizations;
}
// [all groups comes here...]
getGroups = () => {
// return an object for each groups with labels, and the group as "children"
};
}
答案1
得分: 1
自“filteredGuesser”在每次重新渲染时进行计算,触发“useEffect(..., [filterGuesser]”导致重新渲染...所以陷入循环。
最简单的解决方法是使用useMemo
来确保“filteredGuesser”的引用相等。这样,它将在“guessers”更改之前保持引用相同:
const filteredGuesser = useMemo(() =>
guessers
.filter(({canShow}) => canShow)
.map((guesser: any) => {
label: guesser.label ?? guesser.value.options.label,
link: (guesser.operation && `user-management/${guesser.operation}`) ?? guesser.value.name,
})
.sort((a, b) => a.label.localeCompare(b.label))
, [guessers]);
然而,我认为更好的解决方法是重新考虑是否需要以下代码:
useEffect(() =>
...
setList(filteredGuesser)
将准备好的计算存储到状态中对我来说似乎不合理。我认为你最好直接使用filteredGuesser
,而不是将它存储到“list”状态中。
英文:
Since filteredGuesser
calculates on each re-render, which triggers useEffect(..., [filterGuesser]
which causes re-render... so it loops.
The easiest straighforward solution is to ensure reference equality for filteredGuesser
with useMemo
. Then it will be referentially the same until guessers
is changed:
const filteredGuesser = useMemo(() =>
guessers
.filter(({canShow}) => canShow)
.map((guesser: any) => {
label: guesser.label ?? guesser.value.options.label,
link: (guesser.operation && `user-management/${guesser.operation}`) ?? guesser.value.name,
})
.sort((a, b) => a.label.localeCompare(b.label))
, [guessers]);
However, I think the better solution would be reconsider need in
useEffect(() =>
...
setList(filteredGuesser)
This storing ready for use calculation into state does not seem reasonable to me. I think you better use filteredGuesser
directly, instead of storing it into list
state
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论