自定义钩子创建无限循环

huangapple go评论92阅读模式
英文:

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.

自定义钩子:

  1. export const useListGuessers = () => {
  2. const [list, setList] = useState([]);
  3. const hasMarketing = UserHelper.hasAuthorization(AUTHORIZATION_MARKETING);
  4. const hasTechnical = UserHelper.hasAuthorization(AUTHORIZATION_TECHNICAL);
  5. const dashboardGroups = new DashboardGroups({hasMarketing, hasTechnical});
  6. const guessers = [
  7. ...dashboardGroups.appProductGroup(),
  8. ...dashboardGroups.articlesGroup(),
  9. ...dashboardGroups.mediasGroup(),
  10. ...dashboardGroups.productsGroup(),
  11. ...dashboardGroups.orderableProductsGroup(),
  12. ...dashboardGroups.typesGroup(),
  13. ...dashboardGroups.usersGroup(),
  14. ...dashboardGroups.othersGroup(),
  15. ...dashboardGroups.userManagementGroup(),
  16. ];
  17. const filteredGuesser = guessers
  18. .filter(({canShow}) => canShow)
  19. .map((guesser) => {
  20. return {
  21. label: guesser.label ?? guesser.value.options.label,
  22. link: (guesser.operation && `user-management/${guesser.operation}`) ?? guesser.value.name,
  23. };
  24. })
  25. .sort((a, b) => a.label.localeCompare(b.label));
  26. useEffect(() => {
  27. filteredGuesser && setList(filteredGuesser);
  28. }, [filteredGuesser]);
  29. return list;
  30. };

这个类:

  1. export class DashboardGroups {
  2. authorizations: {hasMarketing: boolean; hasTechnical: boolean};
  3. constructor(authorizations: {hasMarketing: boolean; hasTechnical: boolean}) {
  4. this.authorizations = authorizations;
  5. }
  6. // [所有的分组在这里...]
  7. getGroups = () => {
  8. // 返回每个分组的标签和作为“children”的对象
  9. };
  10. }
英文:

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 :

  1. export const useListGuessers = () => {
  2. const [list, setList] = useState([]);
  3. const hasMarketing = UserHelper.hasAuthorization(AUTHORIZATION_MARKETING);
  4. const hasTechnical = UserHelper.hasAuthorization(AUTHORIZATION_TECHNICAL);
  5. const dashboardGroups = new DashboardGroups({hasMarketing, hasTechnical});
  6. const guessers = [
  7. ...dashboardGroups.appProductGroup(),
  8. ...dashboardGroups.articlesGroup(),
  9. ...dashboardGroups.mediasGroup(),
  10. ...dashboardGroups.productsGroup(),
  11. ...dashboardGroups.orderableProductsGroup(),
  12. ...dashboardGroups.typesGroup(),
  13. ...dashboardGroups.usersGroup(),
  14. ...dashboardGroups.othersGroup(),
  15. ...dashboardGroups.userManagementGroup(),
  16. ];
  17. const filteredGuesser = guessers
  18. .filter(({canShow}) => canShow)
  19. .map((guesser: any) => {
  20. return {
  21. label: guesser.label ?? guesser.value.options.label,
  22. link: (guesser.operation && `user-management/${guesser.operation}`) ?? guesser.value.name,
  23. };
  24. })
  25. .sort((a, b) => a.label.localeCompare(b.label));
  26. useEffect(() => {
  27. filteredGuesser && setList(filteredGuesser);
  28. }, [filteredGuesser]);
  29. return list;
  30. };

The class :

  1. export class DashboardGroups {
  2. authorizations: {hasMarketing: boolean; hasTechnical: boolean};
  3. constructor(authorizations: {hasMarketing: boolean; hasTechnical: boolean}) {
  4. this.authorizations = authorizations;
  5. }
  6. // [all groups comes here...]
  7. getGroups = () => {
  8. // return an object for each groups with labels, and the group as "children"
  9. };
  10. }

答案1

得分: 1

自“filteredGuesser”在每次重新渲染时进行计算,触发“useEffect(..., [filterGuesser]”导致重新渲染...所以陷入循环。

最简单的解决方法是使用useMemo来确保“filteredGuesser”的引用相等。这样,它将在“guessers”更改之前保持引用相同:

  1. const filteredGuesser = useMemo(() =>
  2. guessers
  3. .filter(({canShow}) => canShow)
  4. .map((guesser: any) => {
  5. label: guesser.label ?? guesser.value.options.label,
  6. link: (guesser.operation && `user-management/${guesser.operation}`) ?? guesser.value.name,
  7. })
  8. .sort((a, b) => a.label.localeCompare(b.label))
  9. , [guessers]);

然而,我认为更好的解决方法是重新考虑是否需要以下代码:

  1. useEffect(() =>
  2. ...
  3. setList(filteredGuesser)

将准备好的计算存储到状态中对我来说似乎不合理。我认为你最好直接使用filteredGuesser,而不是将它存储到“list”状态中。

关于useMemo的Beta文档

MDN上的引用相等(又称严格相等)

英文:

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:

  1. const filteredGuesser = useMemo(() =>
  2. guessers
  3. .filter(({canShow}) => canShow)
  4. .map((guesser: any) => {
  5. label: guesser.label ?? guesser.value.options.label,
  6. link: (guesser.operation && `user-management/${guesser.operation}`) ?? guesser.value.name,
  7. })
  8. .sort((a, b) => a.label.localeCompare(b.label))
  9. , [guessers]);

However, I think the better solution would be reconsider need in

  1. useEffect(() =>
  2. ...
  3. 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

Beta docs for useMemo

Referential equality aka strict equality on MDN

huangapple
  • 本文由 发表于 2023年2月8日 22:11:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/75387003.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定