Я инициализировал состояние, которое является массивом, и когда я его обновляю, мой компонент не перерисовывается. Вот минимальное доказательство концепции:
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 секунд, это показывает, что числа действительно были обновлены.
Любые мысли о том, почему компонент не обновляется?
Вот КодПесочница с доказательством концепции.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Вам нужно скопировать numbers вот так let old = [...numbers];
useState не обновляет значение, только если оно изменилось, поэтому, если оно было 44 и стало 7, оно будет обновлено. но как он может узнать, изменился ли массив или объект. это по ссылке, поэтому, когда вы это делаете let old = numbers, вы просто передаете ссылку, а не создаете новую
Вы вызываете setNumbers и передаете ему массив, который у него уже есть. Вы изменили одно из его значений, но это все тот же массив, и я подозреваю, что React не видит причин для повторного рендеринга, потому что состояние не изменилось; новый массив - это старый массив.
Один простой способ избежать этого — распространить массив на новый массив:
setNumbers([...old])
ohhhh, поэтому он внутренне сравнивает ссылку вместо проверки на полное равенство. имеет смысл. было бы неплохо, если бы мы могли решить, что перерисовываем сами себя, вместо того, чтобы позволять установщикам состояния делать это в фоновом режиме (возможно, это уже возможно, я понятия не имею)
А как насчет дочернего компонента?
Это лучший ответ. Спасибо!
Влияет ли это решение на производительность?
Гениальное и простое решение
Это была моя проблема... но почему она так себя ведет? Я изменяю строковое свойство верхнего уровня. Даже неглубокая проверка на равенство увидит другой объект....
@adamdabb Он не проверяет содержание массива, он проверяет, является ли сам массив одним и тем же объектом. И даже если бы он проверил содержимое, вы бы получили тот же результат, потому что вы сравнивали бы его с самим собой.
Я понимаю, что это будет работать, потому что модулю обновления состояния всегда передается новая ссылка, но почему бы не исправить фактическую мутацию состояния, вызывающую проблему? Это устраняет симптом, но на самом деле просто скрывает настоящую ошибку.
Мне тоже очень помогло.
У меня такая же проблема с булевым значением. есть идеи?!
Является ли логическое значение свойством объекта? То же самое может произойти.
Спасибо Спасибо спасибо...
Другие уже дали техническое решение. Для тех, кто не понимает, почему это происходит, это потому, что 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])
у меня не сработало
Это работает для меня!! Не могли бы вы объяснить, почему это так работает?
меня устраивает. но в чем причина?
//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);
Не публикуйте просто фрагмент кода. Скорее попробуйте дать немного больше контекста/объяснения, пожалуйста.
Очень хороший ответ. Я делаю сортировку после object.likes, и setRecipes([...recipes.sort((a, b) => (b.likes.length - a.likes.length))]) запускает повторный рендеринг , и все работает как шарм. Спасибо.