Поиск причины неправильного инкремента счётчика в React‑компоненте

useCallback с [] сохраняет первый btnClick, который замкнул count=0, поэтому каждый клик задаёт 1. Второй обработчик отдельно мутирует объект state и передаёт прежнюю ссылку.

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

Что ответить на собеседовании

useCallback с [] сохраняет первый btnClick, который замкнул count=0, поэтому каждый клик задаёт 1. Второй обработчик отдельно мутирует объект state и передаёт прежнюю ссылку.

Подробный разбор

Ответ с пояснениями

Условие

При каждом нажатии на первую кнопку ожидается, что значение будет возрастать на 1, отражая общее количество кликов. В текущей реализации счётчик увеличивается лишь один раз. Необходимо проанализировать код, выяснить, почему состояние не меняется при каждом событии, и предложить исправление.

import { useCallback, useEffect, useMemo, useState } from "react";

export function App() {
  const [count, setCount] = useState(0);
  const [count2, setCount2] = useState({
    name: 'Anton',
  });

  const handleBtnClick2 = () => {
    count2.name = "Alex";
    console.log(count2.name);
    setCount2(count2);
  };

  const btnClick = () => {
    setCount(count + 1);
  };

  const handleBtnClick = useCallback(() => {
    btnClick();
  }, []);

  return (
    <div>
      <button onClick={handleBtnClick}>{count}</button>
      <button onClick={handleBtnClick2}>{count2.name}</button>
      {/* <button onClick={btnClick2}>Compute Heavy</button> */}
      <div />
      <input type="text" />
    </div>
  );
}

Ответ

useCallback с [] сохраняет первый btnClick, который замкнул count=0, поэтому каждый клик задаёт 1. Второй обработчик отдельно мутирует объект state и передаёт прежнюю ссылку.

const handleBtnClick = useCallback(() => {
  setCount(value => value + 1);
}, []);
const handleBtnClick2 = () => {
  setCount2(previous => ({ ...previous, name: 'Alex' }));
};

Функциональный updater получает актуальное значение без зависимости от count. Для объекта создаётся новая ссылка, чтобы React увидел обновление; прямую запись count2.name нужно убрать. Если мемоизация первой кнопке не нужна, useCallback можно вообще не использовать и оставить обычный обработчик с функциональным обновлением.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее