← Назад к вопросам

Как компонент понимает, перерисовались ли пропсы?

1.3 Junior🔥 111 комментариев
#React

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как компонент узнает об изменении props

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

1. Базовый механизм - сравнение props

React автоматически отслеживает изменения props:

function Button({ label, onClick }) {
  console.log('Button rendered with label:', label);
  
  return (
    <button onClick={onClick}>
      {label}
    </button>
  );
}

function Parent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      {/* Первый рендер: label="Click me" */}
      <Button label="Click me" onClick={() => setCount(count + 1)} />
      
      {/* Когда count меняется, props МЕНЯЮТСЯ */}
      {/* React видит: label остался тем же, но onClick теперь ДРУГАЯ функция */}
      {/* Компонент Button перерендерится */}
    </div>
  );
}

2. Как React сравнивает props

React использует поверхностное сравнение (shallow comparison):

// Это РАЗНЫЕ функции - React считает их разными
const func1 = () => console.log('A');
const func2 = () => console.log('A');
console.log(func1 === func2); // false

// Это ОДА функция - React считает их одинаковыми
const func = () => console.log('A');
const refFunc = func;
console.log(func === refFunc); // true

// Для объектов - сравнивает только ссылку, не содержимое
const obj1 = { name: 'Alice' };
const obj2 = { name: 'Alice' };
console.log(obj1 === obj2); // false (разные ссылки)

const obj3 = obj1;
console.log(obj1 === obj3); // true (одна ссылка)

3. useEffect для отслеживания изменения props

Усе useEffect с массивом зависимостей (dependency array):

function Card({ title, description, userId }) {
  // Выполнится при ЛЮБОМ изменении компонента
  console.log('Card rendered');

  // Выполнится только когда title или description меняются
  useEffect(() => {
    console.log('Title or description changed:', title, description);
  }, [title, description]);

  // Выполнится только когда userId меняется
  useEffect(() => {
    console.log('Fetching user data for userId:', userId);
    // Здесь обычно делают fetch запрос
  }, [userId]);

  // Выполнится один раз при монтировании
  useEffect(() => {
    console.log('Component mounted');
    
    return () => {
      console.log('Component unmounted');
    };
  }, []);

  return (
    <div>
      <h2>{title}</h2>
      <p>{description}</p>
    </div>
  );
}

4. Отслеживание ВСЕХ изменений props

function MyComponent(props) {
  // Выполнится КАЖДЫЙ раз когда какой-то prop меняется
  useEffect(() => {
    console.log('Some prop changed');
    console.log('Current props:', props);
  }); // Нет dependency array - выполняется при каждом рендере!

  // ПРАВИЛЬНО - отслеживать конкретные props
  useEffect(() => {
    console.log('Props changed');
  }, [props.id, props.title, props.data]);

  return <div>Component</div>;
}

5. React.memo - оптимизация перерендеров

Если хотите избежать перерендера при неизменных props, используйте React.memo:

// Компонент БЕЗ оптимизации
function Button({ label, onClick }) {
  console.log('Button rendered');
  return <button onClick={onClick}>{label}</button>;
}

// Компонент С оптимизацией
const OptimizedButton = React.memo(function Button({ label, onClick }) {
  console.log('Button rendered');
  return <button onClick={onClick}>{label}</button>;
});

function Parent() {
  const [count, setCount] = useState(0);
  const [other, setOther] = useState('text');

  // Каждый рендер - новая функция
  const handleClick = () => setCount(count + 1);

  return (
    <div>
      {/* При изменении other Button перерендерится ВСЕ РАВНО */}
      {/* Потому что onClick - новая функция каждый раз */}
      <OptimizedButton label="Click" onClick={handleClick} />
      
      <button onClick={() => setOther('new text')}>Change other</button>
    </div>
  );
}

6. useCallback для стабилизации функций

Для React.memo нужны стабильные props:

const OptimizedButton = React.memo(({ label, onClick }) => {
  console.log('Button rendered');
  return <button onClick={onClick}>{label}</button>;
});

function Parent() {
  const [count, setCount] = useState(0);
  const [other, setOther] = useState('text');

  // useCallback мемоизирует функцию
  // Функция пересоздается ТОЛЬКО если count меняется
  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]); // Зависимость: count

  // Или если функция не зависит от state
  const handleStable = useCallback(() => {
    console.log('Clicked');
  }, []); // Никогда не пересоздается

  return (
    <div>
      {/* Теперь при изменении other Button НЕ перерендерится */}
      <OptimizedButton label="Click" onClick={handleClick} />
      
      <button onClick={() => setOther('new text')}>Change other</button>
    </div>
  );
}

7. useMemo для сложных объектов

То же самое для объектов и массивов:

const OptimizedCard = React.memo(({ user, settings }) => {
  console.log('Card rendered');
  return <div>{user.name}</div>;
});

function Parent() {
  const [count, setCount] = useState(0);

  // ПЛОХО - новый объект при каждом рендере
  const user = { name: 'Alice', id: 1 }; // Новый объект!

  // ХОРОШО - мемоизированный объект
  const memoizedUser = useMemo(
    () => ({ name: 'Alice', id: 1 }),
    [] // Пересоздается только если зависимости меняются
  );

  return (
    <div>
      <OptimizedCard user={memoizedUser} />
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

8. Пользовательское сравнение props

Можно определить собственную логику сравнения:

function UserProfile({ id, name, age }) {
  return <div>{name}, {age}</div>;
}

// Стандартное сравнение - re-render если хоть что-то изменилось
const Memoized = React.memo(UserProfile);

// Пользовательское сравнение
const CustomMemo = React.memo(
  UserProfile,
  (prevProps, nextProps) => {
    // Вернуть true = не перерендерить (props одинаковые)
    // Вернуть false = перерендерить (props изменились)
    return (
      prevProps.name === nextProps.name &&
      prevProps.age === nextProps.age
      // Игнорируем id
    );
  }
);

9. DevTools для отслеживания

Используйте React DevTools для отладки:

// 1. Установите React DevTools browser extension
// 2. DevTools -> Profiler
// 3. Запишите взаимодействие
// 4. Посмотрите какие компоненты перерендерились
// 5. На вкладке "Ranked chart" видны самые медленные

// Или добавьте логирование
function MyComponent(props) {
  console.log('Rendered with props:', props);
  
  return (
    <div>
      {/* Если видите одно и то же дважды - бесполезный re-render */}
    </div>
  );
}

Чеклист отслеживания изменений props

  1. useEffect с зависимостями для отслеживания
  2. React.memo для оптимизации компонентов
  3. useCallback для стабильных функций
  4. useMemo для сложных объектов
  5. DevTools для профилирования
  6. Избегайте создания объектов/функций в render
  7. Не забывайте про пустой dependency array []
  8. Тестируйте на производительность

Понимание этого механизма критично для написания эффективного React кода!