Ревью: мемоизация, которая не работает
Вопрос
Перед тобой код на ревью. Скажи вслух, что здесь не так - по убыванию важности.
Читай вслух, как на ревью: сначала что делает код, потом что сломано.
const Row = React.memo(function Row({ item, config, onSelect }) {
return <div onClick={() => onSelect(item.id)}>{item.name}</div>;
});
function List({ items }) {
const [selected, setSelected] = useState(null);
return items.map((item) => (
<Row
key={item.id}
item={item}
config={{ dense: true }}
onSelect={(id) => setSelected(id)}
/>
));
}
Попробуйте сами
Ответьте вслух за 2 минуты, как на собесе. Потом сверьтесь с тезисами: что прозвучало, а что нет.
Показать ответ и разбор
Коротко
Что должно прозвучать в ответе
config - новый объект на каждом рендере, memo всегда видит разные пропсы
onSelect - новая стрелка на каждом рендере, та же проблема
memo здесь не экономит ничего, только добавляет сравнение
config вынести в константу за пределы компонента
onSelect обернуть в useCallback или передать setSelected напрямую
onClick внутри Row тоже создаёт стрелку, но это уже неважно
Что могут спросить следом
- Почему setSelected можно передавать напрямую?
- Как измерить, помогла ли мемоизация?
Разбор
React.memo сравнивает пропсы поверхностно. config={{ dense: true }} создаёт новый
объект на каждом рендере List, onSelect - новую функцию. Сравнение по ссылке всегда
даёт «не равно», поэтому memo не пропускает ни одного рендера - зато честно тратит время
на сравнение. То есть стало медленнее, чем без него.
Починка:
const CONFIG = { dense: true }; // вынести из компонента
const onSelect = useCallback((id) => setSelected(id), []);
setSelected из useState стабилен между рендерами, так что здесь можно передать
его напрямую.
Хороший довесок на ревью: сначала стоит спросить, а нужен ли тут memo вообще. Если строк
двадцать, он не нужен ни в каком виде - это оптимизация по ощущению, а не по замеру.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.