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

Выполнится ли перерисовка если в useState изменить значение на такое же

2.0 Middle🔥 301 комментариев
#React#Оптимизация и производительность

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

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

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

Перерисовка при установке того же значения в useState

Короткий ответ: Нет, перерисовка не происходит, если значение одинаковое. React использует Object.is() для сравнения.

Как работает сравнение в React 18+

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

// Внутри React
setCount(0); // Object.is(0, 0) === true
// Результат: перерисовка НЕ происходит

setCount(1); // Object.is(0, 1) === false
// Результат: перерисовка ПРОИСХОДИТ

Детальный механизм

React использует Object.is() для сравнения

// Object.is() — это строгое сравнение
Object.is(0, 0); // true
Object.is('hello', 'hello'); // true
Object.is(false, false); // true

// Отличие от ===
Object.is(NaN, NaN); // true (отличие от ===)
Object.is(-0, +0); // false (отличие от ===)
Object.is([], []); // false (разные ссылки)

Примеры

Пример 1: Примитивы (числа, строки)

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

  const handleSame = () => {
    console.log('Setting same value: 0');
    setCount(0); // Object.is(0, 0) === true
    // Перерисовка НЕ происходит
  };

  const handleDifferent = () => {
    console.log('Setting different value: 1');
    setCount(1); // Object.is(0, 1) === false
    // Перерисовка ПРОИСХОДИТ
  };

  console.log('Render with count:', count);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleSame}>Set same (0)</button>
      <button onClick={handleDifferent}>Set different (1)</button>
    </div>
  );
}

// Вывод консоли:
// "Render with count: 0"
// Нажимаем "Set same (0)":
// "Setting same value: 0"
// (ничего не печатается, перерисовки нет)
// Нажимаем "Set different (1)":
// "Setting different value: 1"
// "Render with count: 1"

Пример 2: Объекты (перерисовка всегда происходит)

function User() {
  const [user, setUser] = useState({ id: 1, name: 'Alice' });

  const handleSameUser = () => {
    // Object.is({ id: 1, name: 'Alice' }, { id: 1, name: 'Alice' }) === false
    // Разные ссылки в памяти!
    setUser({ id: 1, name: 'Alice' });
    // Перерисовка ПРОИСХОДИТ (новый объект)
  };

  const handleSameReference = () => {
    // Передаём ту же ссылку
    const sameUser = user;
    setUser(sameUser);
    // Object.is(user, sameUser) === true
    // Перерисовка НЕ происходит (та же ссылка)
  };

  console.log('Render with user:', user.name);

  return (
    <div>
      <p>User: {user.name}</p>
      <button onClick={handleSameUser}>Set same object (new)</button>
      <button onClick={handleSameReference}>Set same reference</button>
    </div>
  );
}

// Вывод консоли:
// "Render with user: Alice"
// Нажимаем "Set same object (new)":
// "Render with user: Alice" (перерисовка произошла!)
// Нажимаем "Set same reference":
// (ничего не печатается, перерисовки нет)

Почему это работает так?

Реакт оптимизирует производительность, избегая ненужных перерисовок:

1. setCount(0) -> 0 === 0? ДА -> Пропусти перерисовку
2. setCount(1) -> 1 === 0? НЕТ -> Выполни перерисовку

Это экономит:

  • Время процессора — не пересчитываем виртуальный DOM
  • Память — не создаём новые объекты
  • Электричество — батарея держит дольше

Пример 3: Boolean значения

function Toggle() {
  const [isOpen, setIsOpen] = useState(false);

  const handleSame = () => {
    setIsOpen(false); // Object.is(false, false) === true
    // Перерисовка НЕ происходит
  };

  const handleToggle = () => {
    setIsOpen(true); // Object.is(false, true) === false
    // Перерисовка ПРОИСХОДИТ
  };

  console.log('Rendering with isOpen:', isOpen);

  return (
    <div>
      {isOpen && <p>Content is visible</p>}
      <button onClick={handleSame}>Close (same value)</button>
      <button onClick={handleToggle}>Open</button>
    </div>
  );
}

Пример 4: null и undefined

function Example() {
  const [value, setValue] = useState(null);

  const handleNull = () => {
    setValue(null); // Object.is(null, null) === true
    // Перерисовка НЕ происходит
  };

  const handleUndefined = () => {
    setValue(undefined); // Object.is(null, undefined) === false
    // Перерисовка ПРОИСХОДИТ
  };

  console.log('Rendering with value:', value);

  return (
    <div>
      <button onClick={handleNull}>Set null</button>
      <button onClick={handleUndefined}>Set undefined</button>
    </div>
  );
}

Пример 5: NaN (особый случай)

function Example() {
  const [value, setValue] = useState(NaN);

  const handleNaN = () => {
    setValue(NaN); // Object.is(NaN, NaN) === true (иначе чем обычно!)
    // Перерисовка НЕ происходит
  };

  const handleNumber = () => {
    setValue(0); // Object.is(NaN, 0) === false
    // Перерисовка ПРОИСХОДИТ
  };

  console.log('Rendering with value:', value);

  return (
    <div>
      <button onClick={handleNaN}>Set NaN</button>
      <button onClick={handleNumber}>Set 0</button>
    </div>
  );
}

// Интересно: Object.is(NaN, NaN) === true
// Но в JavaScript обычно NaN !== NaN
// React'ом используется Object.is(), поэтому NaN === NaN

Более сложный пример: Функция обновления

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

  const handleIncrement = () => {
    setCount(prevCount => {
      console.log(`Previous: ${prevCount}, Setting: ${prevCount + 1}`);
      return prevCount + 1;
    });
    // Если prevCount был 0, то вернёт 1
    // Object.is(0, 1) === false
    // Перерисовка ПРОИСХОДИТ
  };

  const handleSetZero = () => {
    setCount(prevCount => {
      const newValue = prevCount === 0 ? 0 : 0; // Всегда 0
      console.log(`Previous: ${prevCount}, Setting: ${newValue}`);
      return newValue;
    });
    // Если count был 0:
    // Object.is(0, 0) === true
    // Перерисовка НЕ происходит
    // 
    // Если count был 5:
    // Object.is(5, 0) === false
    // Перерисовка ПРОИСХОДИТ
  };

  console.log('Render with count:', count);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={handleIncrement}>Increment</button>
      <button onClick={handleSetZero}>Set Zero</button>
    </div>
  );
}

Как это работает внутри React

// Упрощённый код React
function setState(newValue) {
  // 1. Сравниваем старое и новое значение
  if (Object.is(state, newValue)) {
    // 2. Если одинаковые, пропускаем
    return; // Перерисовка НЕ происходит
  }
  
  // 3. Если разные, обновляем
  state = newValue;
  
  // 4. Запускаем перерисовку
  scheduleRender();
}

Best Practices

1. Не создавай новые объекты каждый раз

// ПЛОХО: новый объект каждый раз
const handleClick = () => {
  setUser({ id: 1, name: 'Alice' });
  // Перерисовка ВСЕГДА происходит, даже если значение то же
};

// ХОРОШО: обновляй свойства
const handleClick = () => {
  setUser(prev => ({ ...prev, name: 'Bob' }));
  // Перерисовка происходит только если name изменилась
};

2. Используй useMemo для сложных объектов

function Component() {
  const config = useMemo(() => ({
    timeout: 5000,
    retries: 3
  }), []); // создаётся один раз
  
  setConfig(config);
  // Object.is(config, config) === true
  // Перерисовка НЕ происходит
}

3. Для массивов используй правильные методы

// ПЛОХО: создаёт новый массив каждый раз
setItems([...items, newItem]);
// Перерисовка ПРОИСХОДИТ (новый массив)

// ХОРОШО: но смотри контекст
setItems(prev => [...prev, newItem]);
// Перерисовка ПРОИСХОДИТ (новый массив, это нормально)

Тонкости

1. Что происходит с обновлением состояния?

setCount(0); // count was 0
// Object.is(0, 0) === true
// Перерисовка НЕ происходит ❌

setCount(count + 1); // count was 0
// Object.is(0, 1) === false
// Перерисовка ПРОИСХОДИТ ✓

2. Batching (батчинг обновлений)

function handleClick() {
  setCount(1); // Отложит перерисовку
  setName('Alice'); // Отложит перерисовку
  // React выполнит одну перерисовку для обоих состояний
  // Это называется "batching" (батчинг)
}

Реальный пример: Форма

function Form() {
  const [formData, setFormData] = useState({ name: '', email: '' });

  const handleChange = (e) => {
    const { name, value } = e.target;
    
    setFormData(prev => {
      const updated = { ...prev, [name]: value };
      
      // React сравнит: Object.is(prev, updated)
      // Результат: false (новый объект)
      // Перерисовка ПРОИСХОДИТ (это нужно)
      
      return updated;
    });
  };

  const handleReset = () => {
    setFormData({ name: '', email: '' });
    // Если уже пусто: Object.is({}, {})
    // Результат: false (разные ссылки)
    // Перерисовка ПРОИСХОДИТ (хотя значения одинаковые)
  };

  return (
    <form>
      <input name="name" value={formData.name} onChange={handleChange} />
      <input name="email" value={formData.email} onChange={handleChange} />
      <button onClick={handleReset}>Reset</button>
    </form>
  );
}

Итог

Перерисовка происходит только если:

Object.is(oldValue, newValue) === false

Примеры:

ОперацияПерерисовка?Причина
setCount(0) когда count=0НЕТObject.is(0, 0) === true
setCount(1) когда count=0ДАObject.is(0, 1) === false
setUser({id:1}) каждый разДАНовый объект (новая ссылка)
setUser(prev => prev)НЕТТа же ссылка
setItems([...items, x])ДАНовый массив
setIsOpen(false) когда falseНЕТObject.is(false, false) === true

Это оптимизация React, которая экономит ресурсы и улучшает производительность приложения.