[Перевод] Memo в React
Много статей написано об оптимизации производительности React. В основном, если где-то обновление state происходит медленно, то вам нужно:
Убедиться, что у вас запущен сборка под production. (Сборка dev умышленно медленнее, в крайнем случае — даже на порядок)
Убедиться, что вы не подняли state выше по дереву, чем это необходимо. (Например, размещение state в централизованном хранилище может быть не лучшей идеей)
Запустите React DevTools Profiler, чтобы увидеть, что будет заново отрисовано, оберните самые дорогие [по ресурсам] суб-деревья с помощью memo. (При необходимости добавьте
useMemo)
Этот последний шаг, он раздражает, особенно для промежуточных компонентов, в идеале — компилятор сделает это за вас. В будущем. Возможно.
В этом посте, я хочу поделиться 2 другими подходами. Они так просты, что люди редко понимают, что это улучшает производительность рендера.
Эти подходы дополняют то, что вы уже знаете! Они не заменяют memo или useMemo, но будет хорошее идеей начать с них.
Медленный компонент (искусственно)
Вот компонент с серьёзной проблемой производительности:
import { useState } from 'react';
export default function App() {
let [color, setColor] = useState('red');
return (
setColor(e.target.value)} />
Привет, мир!
);
}
function ExpensiveTree() {
let now = performance.now();
while (performance.now() - now < 100) {
// искусственная задержка -- ничего не делаем 100мс
}
return Я - очень медленное дерево компонентов.
;
}
(Попробуйте здесь)
Проблема в том, что каждый раз, когда цвет меняется внутри App, мы повторно рендерим , в которой мы искусственно добавили задержку, чтобы сделать его медленным.
Я мог бы поместить его в memo() и остановиться на этом, но уже есть много статей, поэтому я не буду тратить на это время. Я хочу показать 2 других решения.
Решение 1: перенесите state вниз
Если вы внимательно посмотрите на код рендеринга, то заметите, что только часть возвращенного дерева компонентов на самом деле заботится о цвете:
export default function App() {
let [color, setColor] = useState('red');
return (
setColor(e.target.value)} />
Привет, мир!
);
}
Так что давайте извлечём эту часть в компонент Form и спустим state в этот компонент:
export default function App() {
return (
<>
>
);
}
function Form() {
let [color, setColor] = useState('red');
return (
<>
setColor(e.target.value)} />
Hello, world!
>
);
}
(Попробуйте здесь)
Теперь, если цвет изменится, то заново рендерится только Form. Задача решена.
Решение 2: поднимите контент вверх
Вышеупомянутой решение не работает, если часть state используется где-то над дорогим деревом. Для примера, давайте предположим, что мы поместили цвет в родительский Hello, world! (Попробуйте здесь) Теперь кажется, что мы не можем просто извлечь нужные части, которые не используют цвет, в другой компонент, поскольку он будет включать родительский Или все же сможем? Поиграйте в этой песочнице и посмотрите, сможете ли вы разобраться. Ответ на удивление прост Ответ Hello, world! (Попробуйте здесь) Мы разделили компонент Части, которые не работают с цветом, остались в компоненте Когда цвет меняется, В результате Прежде, чем применять оптимизацию, такую как Эти подходы интересны тем, что сами по себе они не имеют ничего общего с производительностью. Использование children prop для разделения компонентов обычно упрощает отслеживание потока данных в вашем приложении и сокращает количество prop, которые идут вниз. Повышение производительности в таких случаях — это как вишенка на торте, а не конечная цель. Любопытно, что данный паттерн также откроет больше преимуществ в будущем. Например, когда серверные компоненты готовы к внедрению, наш компонент ColorPicker может получать свои children с сервера. На сервере может работать либо весь компонент Этого не сможет сделать даже Затем, если этого не хватило, то используйте Profiler и добавьте эти Да, возможно Это не новая идея. Это естественная реакция на композиционную модель React. Это достаточно просто, но эти подходы недооценивают, а они заслуживают немного больше любви.export default function App() {
let [color, setColor] = useState('red');
return (
. Теперь без memo не обойтись, не так ли?
export default function App() {
return (
App на 2 части. Части, зависящие от цвета, вместе с самим цветом мы по поместили в ColorPicker. App и передаются в ColorPicker как JSX-контент (children prop).ColorPicker выполняет повторный рендер. Но у него всё ещё есть children prop, который получен из App в прошлый раз, поэтому React не заходит в это суб-дерево. не рендерится заново.Какова же мораль?
memo или useMemo, имеет смысл посмотреть, а может вы сможете отделить части, которые меняются, от частей, которые не меняются., либо его части, и даже изменение state верхнего уровня React «пропускает» на клиент.memo! Но опять же, оба подхода дополняют друг друга. Не забывайте перемещать состояние вниз (а контент вверх).memo.Я мог об этом раньше прочитать где-то ещё?
