Синхронизация против асинхронной

Во-первых, нам нужно установить разницу между синхронным протоколом выполнения кода и асинхронным протоколом выполнения кода в JS.

Когда некоторый код является синхронным, он «синхронизирован» с кодом, выполняемым до и после него: он будет полностью выполнен непосредственно после завершения выполнения предыдущего шага в программе и до любого следующего шага.

Когда некоторый код является асинхронным, он «не синхронизирован», он не следует этому прямому протоколу выполнения. Вызов асинхронного кода на определенном шаге не означает, что он будет полностью выполнен сразу после предыдущего шага.

Кроме того, любой следующий шаг в коде (и последующие шаги после него) может быть выполнен до того, как асинхронный код будет полностью выполнен. Асинхронные функции будут полностью выполнены после очистки стека выполнения. Посмотрите это видео для подробного объяснения выполнения асинхронного кода в javascript (я рекомендую посмотреть все это целиком, но для наших целей посмотрите 10: 24–16: 16 ).

setState () может быть асинхронным

Если мы прочитаем документацию по React (прокрутите до Правильное использование состояния, здесь}, мы увидим, что во многих случаях изменения состояния не происходят сразу после вызова setState(). Вместо этого внутренние механизмы React принимают решение о том, когда действительно обновлять состояние.

Это могут быть миллисекунды, поэтому это может быть трудно сказать, но это происходит не сразу. Фактически, React выбирает лучшее время для обновления состояния и может объединить эти обновления в одно более крупное обновление. В тот раз setState() is async, и React решал, когда запускать процесс. setState() не всегда асинхронный - бывают случаи, когда код реакции изменяет состояние синхронно (на основе некоторых черных ящиков и зависящих от ситуации вещей), но мы не можем полагаться на его синхронность.

Я думаю, что это похоже на использование Git: есть фиксация изменений, а затем фактически проталкивание изменений. Мы пишем код, чтобы «зафиксировать» изменения в состоянии, и внутренняя работа React решает, когда на самом деле «протолкнуть» изменения в состояние, может быть, много / все сразу.

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

Синтетические события и пул

Синтетические события объединяются в «пул», что означает, что объект события будет возвращен / повторно использован React в другом месте нашего приложения. Для этого «объединение» события включает обнуление его свойств после выполнения обратного вызова, инициированного событием DOM (функция, которой передается eObj). Этот «пул» - это функция, встроенная в React по соображениям производительности, о которой мы поговорим в этом разделе.

В Javascript непримитивные типы данных передаются по ссылке. Это означает, что переменные, «содержащие» объекты (непримитивные типы данных, например, массивы, функции и объекты) на самом деле не «содержат» объект, а «содержат» ссылку на адрес в памяти этого объекта.

Когда дело доходит до объекта SyntheticEvent, «объединение в пул» / «повторное использование» - это способ сказать, что браузер не будет выделять память для нового объекта события для каждого события, а вместо этого будет использовать один и тот же объект события в памяти для всех События.

Для этого внутренняя работа React «обнуляет» значения ключей этого объекта после того, как callback / обработчик событий завершил выполнение. Ключи используются повторно, но значение каждого ключа сбрасывается до типа данных null. Этот сброс всех значений объекта на null - вот почему документы React описывают объект события как «обнуляемый».

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

Таким образом, даже если у вас есть переменная, которая указывает на объект SyntheticEvent в памяти (например, let eventCopy = eObj), эта переменная (eventCopy) фактически содержит ссылку на один объект в памяти, который повторно используется браузером. Следовательно, eventCopy указывает на исходный eOb. Как мы уже обсуждали, свойства eObj вскоре аннулируются в качестве подготовки к использованию для хранения данных события для следующего события DOM.

Из-за обнуления свойств объекта события к этим свойствам нельзя получить асинхронный доступ. К тому времени, когда ваши асинхронные функции, скажем, setState(), фактически будут полностью выполнены, и мы попытаемся получить доступ к свойствам события (например, event.currentTarget.value), объект события будет «объединен», а его свойства равны null.

Необходимость event.persist () при вызове setState () - при определенных обстоятельствах

Итак, - много раз - к моменту полного выполнения setState() (и фактического доступа к событию) доступ к eObj был аннулирован. (setState() «может быть асинхронным» согласно документации, но мы не можем полагаться на его синхронность). Переменная, содержащая ссылку на eObj в памяти, по-прежнему содержит правильную ссылку на один объект в памяти, но мы не можем получить доступ к значениям, которые больше не связаны с его ключами. Чтобы объект события не был «объединен» и, следовательно, не обнулялся, мы вызываем event.persist().

Это «удаляет синтетическое событие из пула»: eObj не будет повторно использоваться с более поздними событиями DOM, потому что React будет использовать другую память, чтобы сделать другой объект события вместо текущего eObj. Поскольку событие не обнуляется, оно сохраняет значения своих ключей, и теперь мы можем взаимодействовать с объектом события асинхронно. На данный момент в памяти есть два объекта событий: тот, который мы сохранили и который больше никогда не будет объединяться, и новый объект события, который с этого момента будет объединен и повторно использован.

( Для демонстрации всего того, что обсуждалось до сих пор, клонируйте это репо, установите и запустите, откройте консоль, чтобы увидеть, как работает код. Проверьте комментарии в SimpleComponent .js и не комментируйте console.logs, когда вы используете приложение для изучения управления React объектами событий ).

Примечание. Если синтетическое событие не используется с асинхронными функциями, доступ к событию осуществляется немедленно в ходе выполнения обратного вызова / обработчика события, поэтому event.persist() не требуется.

Альтернатива event.persist ()

Одна альтернатива использованию event.persist(): вы можете хранить нужные данные в переменной (например, const example = event.target). const будет содержать ссылку на объект DOM, полностью независимую от ссылки на объект события. Таким образом, мы сохраняем доступ к цели события и больше не нуждаемся в доступе к цели через объект события.

Также:

this.setState({
 selectedItem: this.state.stuffArray.find(item => item.id ===  e.target.dataset.id)
 }
);

Вышеупомянутое работает из-за того, что значение, связанное с ключом «selectedItem», оценивается / вычисляется до того, как объект будет передан в качестве аргумента для вызова setState(). Независимо от того, что выражение оценивает, это то, что будет связано как значение с ключом selectedItem, и эта оценка происходит по мере определения литерала объекта.

Вышеупомянутое основано на базовом Javascript, показанном ниже:

// simpler example:
let someObj = {a: 1 + 1};
console.log(someObj["a"]);
   // => 2 
// more relevant example:
function addTwoToX(x) {
   return x + 2;
}
let someOtherObj = {b: addTwoToX(2)}
console.log(someOtherObj["b"]);
   // => 4

В обоих приведенных выше случаях, когда мы определяем пару ключ-значение в объекте, ключ связывается с некоторым выражением. Значение, связанное с ключом, - это то, что оценивает оценка, и оно вычисляется по мере определения литерала объекта.

Еще один интересный случай, с которым вы можете столкнуться

Ситуация, когда event.currentTarget равно null даже с event.persist():
https://github.com/facebook/react/issues/2857

Ресурсы