Знайте разницу между передаваемым значением и передаваемой функцией.

Давайте сразу перейдем к сути и зададим два вопроса: почему у нас есть возможность передать функцию в setState и как передача функции решает эту проблему?

setState является асинхронным

Если вы еще не знаете, setState () на самом деле является асинхронным, чтобы React мог объединить несколько вызовов setState () в один повторный рендеринг для повышения производительности. Представьте, что вам нужно повторно визуализировать каждый раз, когда только один компонент вызывает setState (). Это будет невероятно медленно!

Это также причина, по которой у нас еще нет доступа к новому состоянию, когда мы пытаемся сослаться на него сразу после вызова setState ().

const [ count, setCount ] = useState(3)
const onClick = () => {
  setCount(5)
  console.log(count) // first click will print out 3
}
return(<button onClick={onClick}> hello </button>)

Проблема

Но что, если следующее состояние зависит от текущего состояния. Давайте представим простой счетчик, который можно увеличивать нажатием кнопки:

const [ count, setCount ] = useState(0)
const onClick = () => {
  setCount(count + 1)
}
return(
  <div>
    <span> { count } </span>
    <button onClick={onClick}> increment! </button>
  </div>
)

Каждый раз, когда мы щелкаем по кнопке, мы будем увеличивать счетчик с помощью setCount (), который затем будет сигнализировать React о повторном рендеринге этого компонента.

А что, если у пользователя открыто много вкладок и приложений или они могут щелкать так быстро, что React объединяет наши вызовы setCount (), дело в том, что состояние не обновляется, пока пакет не будет завершен.

Итак, если мы представим пакетную обработку двух вызовов setCount (), это будет примерно так:

// let's say count is 5
setCount(count + 1) // count is 5, becomes 6
setCount(count + 1) // count is still 5, becomes 6
// re-renders, count becomes 6

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

Решение

Таким образом, появляются функции, которые спасают положение. Давайте возьмем предыдущий сценарий выше, но вместо этого передадим функцию.

// let's say count is 5
setCount(current => current + 1) // current is 5, becomes 6
setCount(current => current + 1) // current is 6, becomes 7
// re-renders, count becomes 7

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

Счетчик - это очень простой пример, но попробуйте подумать о больших приложениях, которые имеют несколько состояний в разных компонентах. React должен выполнять пакетные вызовы setState (), чтобы наше приложение оставалось плавным и реактивным.

Больше глубины

Проблема заключается в том, что закрытие и React не обновляют наше состояние мгновенно, пока пакет не будет завершен. Потому что, если мы передадим значение в React, он не узнает, как это значение рассчитывается, а будет знать только результат. Итак, давайте снова рассмотрим предыдущий сценарий, в котором мы передаем значение:

// let's say count is 5
setCount(count + 1) --> setCount(6)
setCount(count + 1) --> setCount(6)
// re-renders, count becomes 6

Итак, что мы говорим React, это просто установить наш счетчик на 6. Теперь давайте посмотрим, как это будет выглядеть, когда вместо этого мы передадим функцию.

// let's say count is 5
setCount(current => current + 1) --> setCount(5 => 5 + 1)
setCount(current => current + 1) --> setCount(6 => 6 + 1)
// re-renders, count becomes 7