Как уменьшить количество вызовов useEffect?

Инструмент Google Lighthouse дал моему приложению ужасную оценку производительности, поэтому я провел некоторое расследование. У меня есть компонент под названием Home

внутри Home у меня есть useEffect (только один), который выглядит так

useEffect(() => {
    console.log('rendering in here?') // called 14 times...what?!
    console.log(user.data, 'uvv') // called 13 times...again, What the heck?
}, [user.data])

Я знаю, что вы указываете второй аргумент [], чтобы убедиться, что useEffect вызывается только после изменения данных, но это основная часть, которую я не понимаю. когда я консоль log user.data, первые 4 консольных журнала представляют собой пустые массивы. следующие 9 - это массивы длины 9. Так что, в моей голове, он должен был вызвать его только дважды? один раз для [] и один раз для [].length(9), так что же, черт возьми, происходит?

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

вот как я получаю user.data

const Home = ({ ui, user }) => { // Я передаю это здесь как опору

const mapState = ({ user }) => ({
    user,
})

а затем мой компонент подключен, поэтому я просто передаю его здесь


person Red Baron    schedule 21.05.2020    source источник
comment
Как обновить поле user.data   -  person Shubham Khatri    schedule 21.05.2020
comment
запускает действие redux, которое выполняет запрос ›api gateway› lambda ›firebase для их получения, а затем загружает их в мое хранилище redux   -  person Red Baron    schedule 21.05.2020
comment
user.data - это массив, Array - это ссылочный тип. Поэтому, если вы уничтожите старый массив и создадите новый массив, реакция обнаружит это как изменения.   -  person Kirill Skomarovskiy    schedule 21.05.2020
comment
Итак, user.data поступает из хранилища redux? Как достать из магазина, можно нам селектор показать?   -  person HMR    schedule 21.05.2020
comment
@HMR да добавлю код сейчас   -  person Red Baron    schedule 21.05.2020
comment
@HMR добавили. это просто подключенный компонент. Я не использую селектор. Я наверное должен   -  person Red Baron    schedule 21.05.2020
comment
Вы можете предотвратить повторный рендеринг при использовании повторного выбора, но в настоящее время вы выбираете пользователя, и эффект запускается, когда пользователь. изменения данных. User.data не создается в селекторе, поэтому он должен быть изменен в состоянии. Вы можете посмотреть на редуктор и понять, почему он так часто меняет user.data, или в инструментах redux def, чтобы понять, почему отправляются действия.   -  person HMR    schedule 21.05.2020
comment
@HMR, позволь мне проверить это и скоро свяжусь с тобой. тем временем я прав в своем предположении, что теоретически это должно выполняться только дважды? (один раз в [] и один раз в [] .length (9))   -  person Red Baron    schedule 21.05.2020
comment
ок проверил redux. Я запускаю довольно много действий под нагрузкой. 8 или около того, но только один изменяет user.data, все остальные изменяют либо разные части user, либо разные редукторы. и учитывая, что мой второй аргумент user.data, я не уверен, почему он перерисовывается?   -  person Red Baron    schedule 21.05.2020
comment
хорошо, покопался еще раз и понял, что это может исходить от моего родительского компонента. Мне действительно нужно, чтобы он не вызывал рендеринг больше раз, чем нужно, поскольку это, очевидно, вызывает слишком много детей   -  person Red Baron    schedule 21.05.2020
comment
хорошо понял это, используя response.memo   -  person Red Baron    schedule 21.05.2020


Ответы (2)


Чтобы преодолеть этот сценарий, React Hooks также предоставляет функциональность, называемую useMemo. Вы можете использовать useMemo вместо useEffect, потому что useMemo кэширует экземпляр, который он отображает, и всякий раз, когда он попадает в рендеринг, он сначала проверяет в кеше, был ли какой-либо связанный экземпляр доступен для данных deps. Если это так, то вместо того, чтобы запускать всю функцию, он просто вернуть его из кеша.

person Vicky    schedule 21.05.2020

Это не ответ, но слишком много кода, чтобы поместиться в комментарий. Сначала вы можете регистрировать все действия, которые изменяют user.data, временно заменяя исходный корневой редуктор:

let lastData = {};
const logRootReducer = (state, action) => {
  const newState = rootReducer(state, action);
  if (newState.user.data !== lastData) {
    console.log(
      'action changed data:',
      action,
      newState.user.data,
      lastData
    );
    lastData = newState.user.data;
  }
  return newState;
};

Еще одна причина, по которой user.data продолжает меняться, - это когда вы делаете что-то вроде этого в редукторе:

if (action.type === SOME_TYPE) {
  return {
    ...state,
    user: {
      ...state.user,
      //here data is set to a new array every time
      data: [],
    },
  };
}

Вместо этого вы можете сделать что-то вроде этого:

const EMPTY_DATA = [];
//... other code
data: EMPTY_DATA,

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

Redux devtools также неправильно показывает различия: если вы что-то мутируете в состоянии, devtools покажет их как изменения, но React не увидит их как изменения. Когда вы назначаете новый объект чему-то data:[], тогда redux не будет отображать их как изменения, но React увидит это как изменение.

person HMR    schedule 21.05.2020
comment
принимая другой ответ, поскольку он дал мне то, что мне было нужно, но буду поддерживать его, поскольку он побудил меня поискать в другом месте! не стесняйтесь проголосовать за Q и дайте мне пройти эту отметку в 1000: D - person Red Baron; 21.05.2020
comment
@RedBaron +1, поздравляю с превышением 1000. По-прежнему не имеет смысла, что эффект продолжает вызываться. Независимо от того, сколько раз родительский элемент отображает дочерний элемент, эффект запускается только при изменении state.user.data. - person HMR; 21.05.2020
comment
да, это то, что нужно постоянно изучать, но количество повторных рендеров, по крайней мере, немного снизилось, И основная проблема заключалась в том, что у меня мерцание моей анимации при повторной рендеринге, но это исправило. так что в краткосрочной перспективе я счастлив, в долгосрочной перспективе я должен решить эту проблему. спасибо за помощь, скоро увидимся на этой платформе: D - person Red Baron; 22.05.2020