> Как оптимизировать компонент Child, чтобы он не ререндерился при наборе текста в инпуте (React)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Т-Банк

Стек: React

> Пример ответа

Короткий ответ

Чтобы Child не ререндерился при наборе текста в инпуте, нужно обернуть его в React.memo и убедиться, что его props не меняются. Для этого инпут и его состояние должны быть вынесены в родительский компонент, а Child должен получать только стабильные props. Если Child зависит от пропсов, которые могут меняться, используй useMemo или useCallback для стабилизации ссылок на функции и объекты.

Подробное объяснение

Проблема ререндера Child при наборе текста в инпуте возникает из-за того, что в React при изменении состояния родительского компонента (например, значения инпута) перерендериваются все его дочерние компоненты, даже если их props не изменились. Это поведение по умолчанию.

Основные причины:

  • Состояние инпута (value) хранится в родительском компоненте, и каждый вызов setState вызывает ререндер всего дерева.
  • Если Child не обернут в React.memo, он будет ререндериться при каждом ререндере родителя.
  • Даже с React.memo ререндер произойдет, если props Child содержат новые ссылки (например, встроенные функции или объекты, создаваемые при каждом рендере).

Решение включает:

  1. Вынесение состояния инпута в родительский компонент (или отдельный hook).
  2. Обертка Child в React.memo для поверхностного сравнения props.
  3. Стабилизация пропсов с помощью useCallback для функций и useMemo для объектов/массивов.
  4. Если Child не зависит от пропсов, можно передавать только стабильные значения (например, константы).

На практике

В реальных проектах оптимизация ререндеров важна для производительности, особенно при работе с большими списками, сложными компонентами или частыми обновлениями состояния. Однако не стоит prematurely optimize - React.memo и useCallback имеют свою стоимость (сравнение props, замыкания). Применяй их только если:

  • Child - тяжелый компонент с дорогим рендером.
  • Ребенок часто ререндерится без необходимости.
  • Есть измеримые проблемы с производительностью.

Типичный сценарий: форма с инпутом и списком результатов. Инпут обновляется при каждом нажатии клавиши, но список должен перерендериваться только при изменении данных, а не при вводе текста.

Пример кода

JSX
import React, { useState, useCallback, memo } from 'react';
// Оптимизированный дочерний компонент
const Child = memo(({ data, onAction }) => {
console.log('Child render');
return (
<div>
<span>{data}</span>
<button onClick={onAction}>Action</button>
</div>
);
});
function Parent() {
const [inputValue, setInputValue] = useState('');
const [childData, setChildData] = useState('initial');
// Стабилизируем callback, чтобы ссылка не менялась при каждом рендере
const handleAction = useCallback(() => {
setChildData(prev => prev + ' updated');
}, []);
const handleInputChange = (e) => {
setInputValue(e.target.value);
};
return (
<div>
<input value={inputValue} onChange={handleInputChange} />
{/* Child не будет ререндериться при наборе текста */}
<Child data={childData} onAction={handleAction} />
</div>
);
}

Как отвечать на собеседовании

Начни с объяснения причины ререндера - изменение состояния родителя вызывает ререндер всех детей. Затем предложи React.memo как основное решение. Упомяни, что React.memo делает shallow comparison, поэтому нужно стабилизировать пропсы через useCallback и useMemo. Приведи пример с инпутом и дочерним компонентом. Подчеркни, что оптимизация нужна только при реальных проблемах производительности, иначе это premature optimization. Если спросят про альтернативы, можно упомянуть вынос состояния в контекст или использование useRef для инпута.

Что проверяет интервьюер

  • Понимание механизма ререндера в React и роли React.memo.
  • Умение стабилизировать пропсы с помощью useCallback/useMemo.
  • Знание trade-off между производительностью и сложностью кода.
  • Способность объяснить, когда оптимизация действительно нужна.
  • Понимание shallow comparison и его ограничений.

Типичные ошибки

  • Использование React.memo без стабилизации пропсов - это не даст эффекта, если передаются новые ссылки на функции или объекты.
  • Попытка обернуть в React.memo компонент с children или другими динамическими пропсами, которые всегда новые.
  • Применение useCallback для всех функций без необходимости - это усложняет код и может даже ухудшить производительность из-за накладных расходов на мемоизацию.
  • Забывание, что React.memo не работает с пропсами, которые являются примитивами и меняются по значению (например, строка или число) - в этом случае ререндер все равно произойдет, если значение изменилось.
  • Использование React.memo для компонентов с простым рендером, где overhead от сравнения props превышает выгоду.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью