useMemo¶
useMemo
- это хук React, позволяющий кэшировать результат вычисления между повторными рендерингами.
1 |
|
Описание¶
useMemo(calculateValue, dependencies)
¶
Вызовите useMemo
на верхнем уровне вашего компонента для кэширования вычислений между повторными рендерингами:
1 2 3 4 5 6 7 8 9 |
|
Параметры¶
-
calculateValue
: Функция, вычисляющая значение, которое вы хотите кэшировать. Она должна быть чистой, не принимать аргументов и возвращать значение любого типа. React будет вызывать вашу функцию во время начального рендера. При последующих рендерах React будет возвращать то же значение, еслиdependencies
не изменились с момента последнего рендера. В противном случае он вызоветcalculateValue
, вернет результат и сохранит его, чтобы можно было использовать в дальнейшем. -
dependencies
: Список всех реактивных значений, на которые ссылается кодcalculateValue
. Реактивные значения включают пропсы, состояние, а также все переменные и функции, объявленные непосредственно в теле вашего компонента. Если ваш линтер настроен на React, он проверит, что каждое реактивное значение правильно указано в качестве зависимости. Список зависимостей должен иметь постоянное количество элементов и быть написан inline по типу[dep1, dep2, dep3]
. React будет сравнивать каждую зависимость с предыдущим значением, используя сравнениеObject.is
.
Возвращаемое значение¶
При первоначальном рендере useMemo
возвращает результат вызова calculateValue
без аргументов.
При последующих рендерах он либо вернет уже сохраненное значение из последнего рендера (если зависимости не изменились), либо снова вызовет calculateValue
и вернет результат, который вернул calculateValue
.
Ограничения¶
useMemo
- это хук, поэтому вы можете вызывать его только на верхнем уровне вашего компонента или ваших собственных хуков. Вы не можете вызывать его внутри циклов или условий. Если вам это нужно, создайте новый компонент и переместите состояние в него.- В строгом режиме React будет вызывать вашу функцию вычисления дважды, чтобы помочь вам найти случайные примеси Это поведение только для разработки и не влияет на производство. Если ваша функция вычисления чиста (как и должно быть), это не должно повлиять на вашу логику. Результат одного из вызовов будет проигнорирован.
- React не будет выбрасывать кэшированное значение, если для этого нет особой причины. Например, в разработке React выбрасывает кэш, когда вы редактируете файл вашего компонента. Как в разработке, так и в производстве, React отбрасывает кэш, если ваш компонент приостанавливается во время начального монтирования. В будущем React может добавить больше функций, которые будут использовать преимущества отбрасывания кэша - например, если React в будущем добавит встроенную поддержку виртуализированных списков, то будет иметь смысл отбрасывать кэш для элементов, которые прокручиваются из области просмотра виртуализированной таблицы. Это будет хорошо, если вы полагаетесь на useMemo исключительно как на оптимизацию производительности. В противном случае более подходящим вариантом может быть переменная состояния или ссылка.
Мемоизация
Подобное кэширование возвращаемых значений также известно как мемоизация, поэтому этот хук называется useMemo
.
Использование¶
Пропуск дорогостоящих перерасчетов¶
Чтобы кэшировать вычисления между повторными рендерами, оберните их в вызов useMemo
на верхнем уровне вашего компонента:
1 2 3 4 5 6 7 8 9 |
|
Вам нужно передать две вещи в useMemo
:
- функцию вычисления, которая не принимает аргументов, например
() =>
, и возвращает то, что вы хотели вычислить. - список зависимостей, включающий каждое значение в вашем компоненте, которое используется в расчете.
На первом рендере значение, которое вы получите от useMemo
, будет результатом вызова вашего вычисления.
При каждом последующем рендере React будет сравнивать зависимости с зависимостями, которые вы передали во время последнего рендера. Если ни одна из зависимостей не изменилась (по сравнению с Object.is
), useMemo
вернет значение, которое вы уже вычислили ранее. В противном случае React повторно выполнит расчет и вернет новое значение.
Другими словами, useMemo
кэширует результат вычислений между повторными рендерами, пока не изменятся зависимости.
Давайте рассмотрим пример, чтобы увидеть, когда это полезно.
По умолчанию React будет запускать заново все тело вашего компонента при каждом повторном рендеринге. Например, если этот TodoList
обновит свое состояние или получит новые пропсы от своего родителя, функция filterTodos
будет запущена заново:
1 2 3 4 |
|
Обычно это не является проблемой, поскольку большинство вычислений выполняются очень быстро. Однако, если вы фильтруете или преобразуете большой массив, или выполняете какое-то дорогостоящее вычисление, вы можете захотеть пропустить его, если данные не изменились. Если todos
и tab
те же, что и во время последнего рендеринга, то обернув вычисления в useMemo
, как и ранее, вы сможете повторно использовать visibleTodos
, который вы уже вычислили ранее.
Этот тип кэширования называется мемоизация.
Оптимизация производительности
Вы должны полагаться на useMemo
только в качестве оптимизации производительности. Если ваш код не работает без него, найдите основную проблему и сначала устраните ее. Затем вы можете добавить useMemo
для улучшения производительности.
Как определить, является ли вычисление дорогим?
В общем, если вы не создаете тысячи объектов или не перебираете их в цикле, то, скорее всего, это не дорого. Если вы хотите получить больше уверенности, вы можете добавить консольный журнал, чтобы измерить время, затраченное на часть кода:
1 2 3 |
|
Выполните измеряемое действие (например, введите текст в input
). После этого вы увидите в консоли журналы типа filter array: 0.15ms
в вашей консоли. Если общее время, записанное в журнал, составляет значительную величину (скажем, 1ms
или больше), возможно, имеет смысл запомнить этот расчет. В качестве эксперимента вы можете обернуть расчет в useMemo
, чтобы проверить, уменьшилось ли общее время регистрации для данного взаимодействия или нет:
1 2 3 4 5 |
|
useMemo
не сделает первый рендеринг быстрее. Это только поможет вам пропустить ненужную работу над обновлениями.
Имейте в виду, что ваша машина, вероятно, быстрее, чем у ваших пользователей, поэтому хорошей идеей будет проверить производительность с помощью искусственного замедления. Например, Chrome предлагает для этого опцию CPU Throttling.
Также обратите внимание, что измерение производительности в процессе разработки не даст вам наиболее точных результатов. (Например, если включен Строгий режим, каждый компонент будет отображаться дважды, а не один раз). Чтобы получить наиболее точные результаты, создайте приложение для производства и протестируйте его на устройстве, которое есть у ваших пользователей.
Должны ли вы добавлять useMemo везде?
Если ваше приложение похоже на этот сайт, и большинство взаимодействий являются грубыми (например, замена страницы или целого раздела), мемоизация обычно не нужна. С другой стороны, если ваше приложение больше похоже на редактор рисунков, и большинство взаимодействий являются гранулированными (например, перемещение фигур), то мемоизация может оказаться очень полезной.
Оптимизация с помощью useMemo
полезна лишь в некоторых случаях:
- Вычисления, которые вы помещаете в
useMemo
, заметно медленные, и их зависимости редко меняются. - Вы передаете его как prop компоненту, обернутому в
memo
. Вы хотите пропустить повторный рендеринг, если значение не изменилось. Мемоизация позволяет вашему компоненту перерисовываться только тогда, когда зависимости не меняются. - Значение, которое вы передаете, позже будет использоваться как зависимость какого-нибудь Hook. Например, возможно, от него зависит другое значение вычисления
useMemo
. Или, может быть, вы зависите от этого значения изuseEffect.
.
В других случаях нет никакой пользы от обертывания вычисления в useMemo
. Вреда от этого тоже нет, поэтому некоторые команды предпочитают не думать об отдельных случаях и мемоизировать как можно больше. Недостатком такого подхода является то, что код становится менее читабельным. Кроме того, не вся мемоизация эффективна: одного значения, которое "всегда новое", достаточно, чтобы нарушить мемоизацию для всего компонента.
На практике вы можете сделать ненужной мемоизацию, следуя нескольким принципам:.
- Когда компонент визуально обертывает другие компоненты, позвольте ему принимать JSX в качестве дочерних компонентов Таким образом, когда компонент-обертка обновляет свое собственное состояние, React знает, что его дочерние компоненты не нужно перерисовывать.
- Предпочитайте локальное состояние и не поднимайте состояние вверх дальше, чем это необходимо. Например, не храните переходные состояния, такие как формы и то, наведен ли элемент на вершину вашего дерева, в глобальной библиотеке состояний.
- Сохраняйте чистоту логики рендеринга Если повторный рендеринг компонента вызывает проблему или приводит к заметным визуальным артефактам, это ошибка в вашем компоненте! Исправьте ошибку вместо того, чтобы добавлять мемоизацию.
- Избегайте ненужных Эффектов, обновляющих состояние. Большинство проблем с производительностью в приложениях React вызвано цепочками обновлений, исходящих от Эффектов, которые заставляют ваши компоненты рендериться снова и снова.
- Попробуйте удалить ненужные зависимости из ваших Эффектов Например, вместо мемоизации часто проще переместить какой-то объект или функцию внутрь Эффекта или за пределы компонента.
Если конкретное взаимодействие все еще кажется нестабильным, используйте профилировщик React Developer Tools, чтобы увидеть, какие компоненты больше всего выиграют от мемоизации, и добавьте мемоизацию там, где это необходимо. Эти принципы облегчают отладку и понимание ваших компонентов, поэтому следовать им полезно в любом случае. В долгосрочной перспективе мы изучаем автоматическое выполнение гранулярной мемоизации, чтобы решить эту проблему раз и навсегда.
Разница между useMemo и вычислением значения напрямую¶
1. Пропуск пересчета с useMemo
В этом примере реализация filterTodos
искусственно замедлена, чтобы вы могли увидеть, что происходит, когда какая-то функция JavaScript, вызываемая вами во время рендеринга, действительно медленная. Попробуйте переключить вкладки и переключить тему.
Переключение вкладок кажется медленным, потому что это заставляет замедленный filterTodos
повторно выполняться. Это ожидаемо, потому что вкладка
изменилась, и поэтому все вычисления нужно выполнить заново. (Если вам интересно, почему он выполняется дважды, это объясняется здесь).
Переключите тему. Благодаря useMemo
, это быстро, несмотря на искусственное замедление! Медленный вызов filterTodos
был пропущен, потому что todos
и tab
(которые вы передаете как зависимости useMemo
) не изменились с момента последнего рендера.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
2. Всегда пересчитывает значение
В этом примере реализация filterTodos
также искусственно замедлена, чтобы вы могли увидеть, что происходит, когда какая-то функция JavaScript, которую вы вызываете во время рендеринга, действительно медленная. Попробуйте переключить вкладки и переключить тему.
В отличие от предыдущего примера, переключение темы теперь также происходит медленно! Это происходит потому, что в этой версии отсутствует вызов useMemo
, поэтому искусственно замедляющий работу filterTodos
вызывается при каждом повторном рендеринге. Он вызывается, даже если изменилась только тема
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
Однако, вот тот же код с искусственным замедлением. Отсутствие useMemo
ощутимо или нет?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Довольно часто код без мемоизации работает нормально. Если ваши взаимодействия достаточно быстрые, то мемоизация может и не понадобиться.
Вы можете попробовать увеличить количество элементов todo в utils.js
и посмотреть, как изменится поведение. Этот конкретный расчет был не очень дорогим изначально, но если количество todos значительно вырастет, то большая часть накладных расходов будет приходиться на повторное отображение, а не на фильтрацию. Читайте ниже, чтобы узнать, как можно оптимизировать повторный просмотр с помощью useMemo
.
Пропуск повторного рендеринга компонентов¶
В некоторых случаях useMemo
также может помочь вам оптимизировать производительность повторного рендеринга дочерних компонентов. Чтобы проиллюстрировать это, допустим, компонент TodoList
передает visibleTodos
в качестве параметра дочернему компоненту List
:
1 2 3 4 5 6 7 8 |
|
Вы заметили, что при переключении пропса theme
приложение на мгновение замирает, но если убрать <List />
из JSX, то все работает быстро. Это говорит о том, что стоит попробовать оптимизировать компонент List
.
По умолчанию, когда компонент рендерится, React рекурсивно рендерит все его дочерние элементы. Вот почему, когда TodoList
рендерится с другой theme
, компонент List
также рендерится. Это хорошо для компонентов, которым не требуется много вычислений для повторного рендеринга. Но если вы убедились, что повторный рендеринг медленный, вы можете сказать List
пропустить повторный рендеринг, когда его пропсы такие же, как и при последнем рендере, обернув его в memo
:
1 2 3 4 5 |
|
После этого изменения List
будет пропускать повторный рендеринг, если все его пропсы те же, что и при последнем рендеринге. Вот где кэширование вычислений становится важным! Представьте, что вы вычислили visibleTodos
без useMemo
:
1 2 3 4 5 6 7 8 9 10 11 |
|
В приведенном выше примере функция filterTodos
всегда создает разный массив, подобно тому, как объектный литерал {}
всегда создает новый объект. Обычно это не является проблемой, но это означает, что пропс List
никогда не будет одинаковым, и ваша оптимизация memo
не будет работать. Вот здесь-то и пригодится useMemo
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Вернув расчет visibleTodos
в useMemo
, вы гарантируете, что он будет иметь одно и то же значение между повторными рендерингами (пока не изменятся зависимости). Вы не обязаны обертывать вычисления в useMemo
, если только вы не делаете это по какой-то конкретной причине. В данном примере причина в том, что вы передаете его компоненту, обернутому в memo
, и это позволяет ему пропустить повторный рендеринг. Есть еще несколько причин добавить useMemo
, которые описаны далее на этой странице.
Мемоизация отдельных узлов JSX
Вместо того чтобы обертывать List
в memo
, можно обернуть сам JSX-узел <List />
в useMemo
:
1 2 3 4 5 6 7 8 9 10 11 |
|
Поведение будет таким же. Если visibleTodos
не изменился, List
не будет перерендерирован.
Узел JSX типа <List items={visibleTodos} />
- это объект типа { type: List, props: { items: visibleTodos } }
. Создание этого объекта очень дешево, но React не знает, совпадает ли его содержимое с прошлым разом или нет. Поэтому по умолчанию React перерендерит компонент List
.
Однако, если React видит тот же самый JSX, что и во время предыдущего рендеринга, он не будет пытаться перерендерить ваш компонент. Это происходит потому, что узлы JSX являются неизменяемыми. Объект узла JSX не мог измениться с течением времени, поэтому React знает, что можно пропустить повторный рендеринг. Однако, чтобы это сработало, узел должен фактически быть тем же объектом, а не просто выглядеть одинаково в коде. Именно это и делает useMemo
в данном примере.
Ручное обертывание JSX-узлов в useMemo
не очень удобно. Например, вы не можете сделать это условно. Обычно поэтому вместо обертывания JSX-узлов вы оборачиваете компоненты с помощью memo
.
Разница между пропуском рендеринга и постоянным рендерингом¶
1. Пропуск повторного рендеринга с useMemo
и memo
В этом примере компонент List
искусственно замедлен, чтобы вы могли увидеть, что происходит, когда рендеринг компонента React действительно медленный. Попробуйте переключить вкладки и переключить тему.
Переключение вкладок кажется медленным, потому что это заставляет замедленный List
повторно рендериться. Это ожидаемо, потому что вкладка
изменилась, и вам нужно отразить новый выбор пользователя на экране.
Далее попробуйте переключить тему. Благодаря useMemo
вместе с memo
это происходит быстро, несмотря на искусственное замедление! Список List
пропустил повторный рендеринг, потому что массив visibleItems
не изменился с момента последнего рендеринга. Массив visibleItems
не изменился, потому что todos
и tab
(которые вы передаете в качестве зависимостей в useMemo
) не изменились с момента последнего рендеринга.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
2. Всегда перерендеринг компонента
В этом примере реализация List
также искусственно замедлена, чтобы вы могли увидеть, что происходит, когда какой-либо компонент React, который вы рендерите, действительно медленный. Попробуйте переключить вкладки и переключить тему.
В отличие от предыдущего примера, переключение темы теперь также происходит медленно! Это происходит потому, что в этой версии нет вызова useMemo
, поэтому visibleTodos
- это всегда другой массив, и замедленный компонент List
не может пропустить повторный рендеринг.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Однако, вот тот же код с искусственным замедлением. Отсутствие useMemo
ощутимо или нет?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
1 2 3 4 5 6 7 8 9 10 11 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Довольно часто код без мемоизации работает нормально. Если ваши взаимодействия достаточно быстрые, мемоизация не нужна.
Помните, что вам нужно запустить React в производственном режиме, отключить React Developer Tools и использовать устройства, похожие на те, которые есть у пользователей вашего приложения, чтобы получить реальное представление о том, что на самом деле замедляет работу вашего приложения.
Мемоизация зависимости от другого хука¶
Предположим, у вас есть вычисление, которое зависит от объекта, созданного непосредственно в теле компонента:
1 2 3 4 5 6 7 8 |
|
Такая зависимость от объекта сводит на нет смысл мемоизации. При повторном рендеринге компонента весь код, находящийся непосредственно в теле компонента, запускается снова. Так как searchOptions
является зависимостью вашего вызова useMemo
, и каждый раз она разная, React знает, что зависимости разные, и каждый раз пересчитывает searchItems
.
Чтобы исправить это, вы можете мемоизировать объект searchOptions
самостоятельно перед передачей его в качестве зависимости:
1 2 3 4 5 6 7 8 9 10 11 |
|
В приведенном выше примере, если text
не изменился, объект searchOptions
также не изменится. Однако еще лучшим решением является перемещение объявления объекта searchOptions
внутрь функции вычисления useMemo
:
1 2 3 4 5 6 7 8 9 10 |
|
Теперь ваш расчет зависит непосредственно от text
(который является строкой и не может "случайно" стать другим).
Мемоизация функции¶
Предположим, что компонент Form
обернут в memo
. Вы хотите передать ему функцию в качестве пропса:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Подобно тому, как {}
создает другой объект, объявления функций типа function() {}
и выражения типа () => {}
создают разную функцию при каждом повторном рендеринге. Само по себе создание новой функции не является проблемой. Это не то, чего нужно избегать! Однако, если компонент Form
мемоизирован, предположительно вы хотите пропустить его повторное отображение, когда ни один пропс не изменился. Пропс, который всегда меняется, уничтожит смысл мемоизации.
Чтобы мемоизировать функцию с помощью useMemo
, ваша вычислительная функция должна возвращать другую функцию:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Это выглядит неуклюже! Мемоизация функций достаточно распространена, поэтому в React есть встроенный хук специально для этого. Оберните ваши функции в useCallback
вместо useMemo
, чтобы избежать необходимости писать дополнительную вложенную функцию:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Два приведенных выше примера полностью эквивалентны. Единственное преимущество useCallback
в том, что он позволяет вам избежать написания дополнительной вложенной функции внутри. Больше она ничего не делает. Подробнее об useCallback
.
Устранение неполадок¶
Мой расчет выполняется дважды при каждом рендере¶
В Строгом режиме React будет вызывать некоторые из ваших функций дважды вместо одного раза:
1 2 3 4 5 6 7 8 9 10 |
|
Это ожидаемо и не должно нарушать ваш код.
Это поведение только для разработчиков помогает вам поддерживать чистоту компонентов. React использует результат одного из вызовов и игнорирует результат другого вызова. Пока ваш компонент и функции вычисления чисты, это не должно влиять на вашу логику. Однако если они случайно оказались нечистыми, это поможет вам заметить и исправить ошибку.
Например, эта нечистая функция вычисления мутирует массив, который вы получили в качестве пропса:
1 2 3 4 5 6 |
|
React вызывает вашу функцию дважды, поэтому вы заметите, что todo
добавляется дважды. Ваш расчет не должен изменять существующие объекты, но можно изменять любые новые объекты, созданные вами во время расчета. Например, если функция filterTodos
всегда возвращает другой массив, вы можете изменить этот массив:
1 2 3 4 5 6 |
|
Прочитайте Поддержание чистоты компонентов, чтобы узнать больше о чистоте.
Также ознакомьтесь с руководствами по обновлению объектов и обновлению массивов без мутации.
Мой вызов useMemo
должен вернуть объект, но возвращает undefined
¶
Этот код не работает:
1 2 3 4 5 |
|
В JavaScript () => {
начинает тело стрелочной функции, поэтому скобка {
не является частью вашего объекта. Именно поэтому она не возвращает объект и приводит к ошибкам. Вы можете исправить это, добавив скобки типа ({
и })
:
1 2 3 4 5 6 7 8 |
|
Однако это все еще запутанно и слишком легко для того, чтобы кто-то мог нарушить его, убрав круглые скобки.
Чтобы избежать этой ошибки, пишите оператор return
в явном виде:
1 2 3 4 5 6 7 |
|
Каждый раз, когда мой компонент рендерится, вычисления в useMemo
запускаются заново¶
Убедитесь, что вы указали массив зависимостей в качестве второго аргумента!
Если вы забудете массив зависимостей, useMemo
будет каждый раз запускать расчет заново:
1 2 3 4 5 6 7 |
|
Это исправленная версия, передающая массив зависимостей в качестве второго аргумента:
1 2 3 4 5 6 7 8 |
|
Если это не помогло, то проблема в том, что по крайней мере одна из ваших зависимостей отличается от предыдущего рендера. Вы можете отладить эту проблему, вручную записав логи зависимостей в консоль:
1 2 3 4 5 |
|
Затем вы можете щелкнуть правой кнопкой мыши на массивах из разных рендеров в консоли и выбрать "Store as a global variable" для обоих. Предположив, что первый массив был сохранен как temp1
, а второй - как temp2
, вы можете использовать консоль браузера, чтобы проверить, является ли каждая зависимость в обоих массивах одинаковой:
1 2 3 4 5 6 7 8 |
|
Когда вы обнаружите, какая зависимость нарушает мемоизацию, либо найдите способ удалить ее, либо мемоизируйте и ее.
Мне нужно вызвать useMemo
для каждого элемента списка в цикле, но это не разрешено¶
Предположим, что компонент Chart
обернут в memo
. Вы хотите пропустить повторное отображение каждого Chart
в списке при повторном отображении компонента ReportList
. Однако вы не можете вызвать useMemo
в цикле:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Вместо этого извлеките компонент для каждого элемента и мемоизируйте данные для отдельных элементов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
В качестве альтернативы можно убрать useMemo
и вместо этого обернуть сам Report
в memo
. Если параметр item
не меняется, Report
пропускает повторное отображение, поэтому Chart
тоже пропускает повторное отображение:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Источник — https://react.dev/reference/react/useMemo