Почему useState не запускает повторный рендеринг?

Я инициализировал состояние, которое является массивом, и когда я его обновляю, мой компонент не перерисовывается. Вот минимальное доказательство концепции:

function App() {
  const [numbers, setNumbers] = React.useState([0, 1, 2, 3]);
  console.info("rendering...");
  return (
    <div className = "App">
      {numbers.map(number => (
        <p>{number}</p>
      ))}
      <input
        type = "text"
        value = {numbers[0].toString()}
        onChange = {newText => {
          let old = numbers;
          old[0] = 1;
          setNumbers(old);
        }}
      />
    </div>
  );
}

Судя по этому коду, кажется, что ввод должен содержать число 0 для запуска, и каждый раз, когда он изменяется, состояние также должно меняться. После ввода «02» во входных данных компонент приложения не выполняет повторную визуализацию. Однако, если я добавлю setTimeout в функцию onChange, которая выполняется через 5 секунд, это показывает, что числа действительно были обновлены.

Любые мысли о том, почему компонент не обновляется?

Вот КодПесочница с доказательством концепции.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
158
0
93 227
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Вам нужно скопировать numbers вот так let old = [...numbers];

useState не обновляет значение, только если оно изменилось, поэтому, если оно было 44 и стало 7, оно будет обновлено. но как он может узнать, изменился ли массив или объект. это по ссылке, поэтому, когда вы это делаете let old = numbers, вы просто передаете ссылку, а не создаете новую

Ответ принят как подходящий

Вы вызываете setNumbers и передаете ему массив, который у него уже есть. Вы изменили одно из его значений, но это все тот же массив, и я подозреваю, что React не видит причин для повторного рендеринга, потому что состояние не изменилось; новый массив - это старый массив.

Один простой способ избежать этого — распространить массив на новый массив:

setNumbers([...old])

Очень хороший ответ. Я делаю сортировку после object.likes, и setRecipes([...recipes.sort((a, b) => (b.likes.length - a.likes.length))]) запускает повторный рендеринг , и все работает как шарм. Спасибо.

Stephan Bakkelund Valois 26.07.2020 19:35

ohhhh, поэтому он внутренне сравнивает ссылку вместо проверки на полное равенство. имеет смысл. было бы неплохо, если бы мы могли решить, что перерисовываем сами себя, вместо того, чтобы позволять установщикам состояния делать это в фоновом режиме (возможно, это уже возможно, я понятия не имею)

Alan 12.10.2020 15:28

А как насчет дочернего компонента?

digz6666 06.04.2021 18:03

Это лучший ответ. Спасибо!

Calin Vlasin 31.05.2021 04:19

Влияет ли это решение на производительность?

ripytide 02.06.2021 18:03

Гениальное и простое решение

Robby Hoover 02.08.2021 06:19

Это была моя проблема... но почему она так себя ведет? Я изменяю строковое свойство верхнего уровня. Даже неглубокая проверка на равенство увидит другой объект....

adamdabb 15.08.2021 19:49

@adamdabb Он не проверяет содержание массива, он проверяет, является ли сам массив одним и тем же объектом. И даже если бы он проверил содержимое, вы бы получили тот же результат, потому что вы сравнивали бы его с самим собой.

ray hatfield 17.08.2021 18:47

Я понимаю, что это будет работать, потому что модулю обновления состояния всегда передается новая ссылка, но почему бы не исправить фактическую мутацию состояния, вызывающую проблему? Это устраняет симптом, но на самом деле просто скрывает настоящую ошибку.

Brian Thompson 25.10.2021 21:26

Мне тоже очень помогло.

dbld 02.12.2021 14:35

У меня такая же проблема с булевым значением. есть идеи?!

kamiyar 13.12.2021 14:06

Является ли логическое значение свойством объекта? То же самое может произойти.

ray hatfield 13.12.2021 16:30

Спасибо Спасибо спасибо...

sjclark76 28.02.2022 20:25

Другие уже дали техническое решение. Для тех, кто не понимает, почему это происходит, это потому, что setSomething() повторно отображает компонент только тогда и только тогда, когда предыдущее и текущее состояние различаются. Поскольку массивы в javascript являются ссылочными типами, если вы редактируете элемент массива в js, он все равно не изменяет ссылку на исходный массив. В глазах js эти два массива одинаковы, хотя исходное содержимое внутри этих массивов разное. Вот почему setSomething() не может обнаружить изменения, внесенные в старый массив.

Обратите внимание, что если вы используете компоненты класса и обновляете состояние с помощью setState(), тогда компонент всегда будет обновляться независимо от того, изменилось состояние или нет. Таким образом, вы можете изменить свой функциональный компонент на компонент класса в качестве решения. Или следуйте ответам, предоставленным другими.

useState — это хук React, который обеспечивает функциональность наличия состояния в функциональном компоненте. Обычно он информирует React о необходимости повторного рендеринга компонента при изменении переменных useState.

{
      let old = numbers;
      old[0] = 1;
      setNumbers(old);
}

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

Чтобы преодолеть, используйте приведенный ниже хак, который не будет копировать ссылку, а будет глубокой копией (копирует значения)

{
      let old = JSON.parse(JSON.stringify(numbers));
      old[0] = 1;
      setNumbers(old);
}

Удачного кодирования :)

Вы можете изменить состояние следующим образом

const [state, setState] = ({})
setState({...state})

или если ваше состояние массив, вы можете изменить это так

const [state, setState] = ([])
setState([...state])

у меня не сработало

Willian 30.09.2021 05:39

Это работает для меня!! Не могли бы вы объяснить, почему это так работает?

wilfredonoyola 12.11.2021 19:18

меня устраивает. но в чем причина?

Siva Sankaran 02.01.2022 09:22
  //define state using useState hook
  const [numbers, setNumbers] = React.useState([0, 1, 2, 3]);

  //copy existing numbers in temp
  let tempNumbers = [...numbers];
  // modify/add no
  tempNumbers.push(4);
  tempNumbers[0] = 10;
  // set modified numbers
  setNumbers(tempNumbers);

Не публикуйте просто фрагмент кода. Скорее попробуйте дать немного больше контекста/объяснения, пожалуйста.

kissu 22.06.2021 16:42

Другие вопросы по теме