ReactJS состояние запускается в цикле, когда я использую обычную функцию селектора

#javascript #reactjs #use-effect #redux-selector

#javascript #reactjs #использование-эффект #redux-селектор

Вопрос:

Основная проблема: я пытался использовать функцию внутри селектора для повторного преобразования данных и присоединения к другой переменной, в данном случае к моей группе, и объединять их с дочерними элементами в качестве элементов, проблема в том, что функция вызывается каждый раз в бесконечном цикле, несмотря на то, что состояние не изменяется.

У меня есть этот селектор: const groups = useSelector(state => selectProductGroups(state));

И функция такая:

   const groups = state.PlatformsReducer.groups;
  const items = state.PlatformsReducer.items;
  return groups.reduce((ac, g) => {
    g.items = items.filter(i => i.groupId == g.productNumber);
    if (ac[g.platformId]) {
      ac[g.platformId].push(g);
    } else {
      ac[g.platformId] = [g];
    }
    return ac;
  }, {});
};

So when I use a useEffect to detect if the groups variable has changed the useEffect is triggered in a loop despite the variable groups still empty.

Do you know why? or How to prevent this.

I now the problem is the function in the selector, but I don't know how to prevent this case.
 

Комментарии:

1. Я думаю, что лучший способ приблизиться к этому — использовать два отдельных useSelector в сочетании с useMemo пользовательским хуком. Насколько я понимаю, under the hood useSelector создает подписку на хранилище, каждый раз, когда хранилище изменяется, оно проверяет, совпадает ли результат предоставленной функции с текущим результатом в состоянии (сравнение ссылок) и обновляет внутренние useState , которые он использует, только если они отличаются. Это означало бы, что он все равно будет запускать вашу сложную функцию каждый раз при изменении хранилища, даже если она не связана с той частью хранилища, которая изменилась.

Ответ №1:

Это связано с тем, что useSelector хук делает внутри.

useSelector запускает ваш селектор и проверяет, совпадает ли результат с ранее полученным результатом (сравнение ссылок). Если результаты отличаются, то новый результат сохраняется и выполняется принудительный повторный запуск. Если результаты совпадают, старый результат не заменяется и повторный запуск не запускается.

Это означает, что каждый раз, когда хранилище обновляется, даже если это не связанная часть состояния, ваша сложная функция будет выполняться, чтобы определить, изменился ли результат. В вашем случае это всегда новая ссылка и, следовательно, всегда изменение.

Я думаю, что лучший способ справиться с этим — сделать ваши селекторы как можно более простыми или использовать какую-либо форму более сложной запоминания, например, предоставляемую reselect .

Ниже приведен пример того, как вы могли бы упростить свои селекторы, но при этом получить простой способ повторного использования выбора группы продуктов с помощью пользовательского хука.

 const useProductGroups = () => {
    // Get groups from the store. 
    // As the selector does not create a new object it should only 
    // trigger a rerender when groups changes in the store.
    const groups = useSelector(state => state.PlatformsReducer.groups);

    // Get items from the store, 
    // As the selector does not create a new object it should only 
    // trigger a rerender when items changes in the store.
    const items = useSelector(state => state.PlatformsReducer.items);

    // Reduce the group collection as desired inside of a useMemo 
    // so that the reduction only occurs when either items or groups 
    // changes.
    const productGroups = useMemo(() => {
        return groups.reduce((ac, g) => {
            g.items = items.filter(i => i.groupId == g.productNumber);

            if (ac[g.platformId]) {
                ac[g.platformId].push(g);
            } else {
                ac[g.platformId] = [g];
            }

            return ac;
        }, {});
    }, [groups, items] /* dependency array on items / groups */);

    // return the calculated product groups
    return productGroups;
}
 

Затем вы можете использовать пользовательский хук в своих функциональных компонентах:

 const groups = useProductGroups();