自定义钩子创建无限循环

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

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”状态中。

关于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:

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

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:

确定