Тот факт, что эти переменные возвращаются из хука React, не заставляет их вести себя иначе, чем любую другую переменную JS. const
является константой; это не может измениться. Рассмотрим следующий пример без React.
function logValue(input) {
console.log(input);
}
const value = 0;
logValue(value + 5);
logValue(value + 5);
logValue(value + 5);
Он регистрирует 5 каждый раз. Это то, что вы ожидаете, верно? Поскольку value
является константой и всегда будет равно 0. Вы можете подумать, что это не то же самое, потому что вы не ведете журнал, а обновляете. Но это действительно не отличается. Функции обновления состояния не просто обновляют переменную типа count = 5
(подсказка: это сломается, см. ниже).
const value = 0;
function updateValue(input) {
value = input;
}
updateValue(value + 5);
Вместо этого он отслеживает, какое значение должно быть при следующем рендеринге, и когда функциональный компонент вызывается снова, константе с отслеживанием состояния присваивается это новое значение.
Таким образом, даже если вы вызываете функцию обновления более одного раза, значение, которое вы отправляете для обновления, равно всегда 5.
Что касается функционального обновления (setCount(count => count + 5)
), здесь count
относится не к константе состояния, а к параметру count
(название сбивает с толку, поскольку переменная состояния затеняется параметром). Этот параметр гарантированно содержит самое последнее значение состояния (для помощь с асинхронными ограничениями). Вот почему второй набор работает так, как ожидалось.
Вот более полный пример с журналами, которые показывают, когда что-то действительно вызывается, и их значения на протяжении всего процесса:
const {useState, useEffect} = React;
const Example = () => {
const [count, setCount] = useState(0);
const regularUpdate = () => {
console.log('count before update:', count);
setCount(count + 5);
console.log('count after 1 update:', count);
setCount(count + 5);
console.log('count after 2 updates:', count);
setCount(count + 5);
console.log('count after 3 updates:', count);
}
const functionalUpdate = () => {
console.log('count before update:', count);
setCount(count => {
console.log('count inside 1st update:', count);
return count + 5
});
setCount(count => {
console.log('count inside 2nd update:', count);
return count + 5
});
setCount(count => {
console.log('count inside 3rd update:', count);
return count + 5
});
console.log('count after updates:', count);
}
return (
<div>
<h1>Count: {count}</h1>
<button onClick={regularUpdate}>Run normal updates</button>
<button onClick={functionalUpdate}>Run functional updates</button>
<button onClick={() => {setCount(0); console.clear();}}>Reset</button>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Стоит обратить внимание на порядок журналов при запуске функциональных обновлений. Обратите внимание, что счетчик после обновлений регистрируется перед любым из журналов, внутренних для функциональных обновлений, и что он по-прежнему содержит необновленное значение.
person
Brian Thompson
schedule
18.11.2020