Базовый сценарий
У меня есть компонент, контролируемый текстовым полем React, чье onChange
событие в конечном итоге запускает вызов AJAX для API на стороне сервера. Результаты этого вызова могут потенциально изменить значение текстового поля. Итак, в обратном вызове AJAX есть вызов на setState
.
Основная проблема
У меня возникают проблемы с поиском способа плавного, последовательного обновления этого значения, когда изменения вносятся во входные данные до завершения вызова AJAX. Есть два подхода, которые я пробовал до сих пор. Основное отличие заключается в в том, как в конечном итоге происходит вызов AJAX.
Подход 1
Моя первая попытка вызывает setState
с немедленно введенными данными, что в конечном итоге вызывает повторную визуализацию и componentDidUpdate
. Затем последний выполняет вызов AJAX при условии, что данные о состоянии отличаются.
handleChange(event) {
const inputTextValue = event.target.value;
setState({ inputText: inputTextValue }); // will trigger componentDidUpdate
}
componentDidUpdate(lastProps, lastState) {
const inputTextValue = this.state.inputText;
if (lastState.inputText !== inputTextValue) { // string comparison to prevent infinite loop
$.ajax({
url: serviceUrl,
data: JSON.stringify({ inputText: inputTextValue })
// set other AJAX options
}).done((response) => {
setState({ inputText: response.validatedInputTextValue }); // will also trigger componentDidUpdate
});
}
}
Преимущество этого подхода заключается в быстром обновлении состояния для отображения немедленного ввода пользователя. Однако, если два ввода выполняются быстро, происходит следующая последовательность действий:
Обработчик событий 1 срабатывает со значением '1'
- Обработчик вызовов 1
setState
со значением '1'
- Компонент повторно отображается после изменения состояния
componentDidUpdate
срабатывает от повторного рендеринга
- Значение
'1'
отличается от последнего значения, поэтому
- AJAX вызов 1 выполнен со значением
'1'
Во время вызова AJAX 1 обработчик события 2 запускается со значением '12'
- Обработчик 2 вызовов
setState
со значением '12'
componentDidUpdate
вызвано повторным рендерингом
- Значение
'12'
отличается от '1'
, поэтому
- вызов AJAX 2 выполнен со значением
'12'
Во время выполнения вызова AJAX 2 вызов AJAX 1 возвращается со значением '1'
- AJAX callback 1 вызывает
setState
со значением '1'
componentDidUpdate
вызвано повторным рендерингом
- Значение
'1'
отличается от '12'
, поэтому
- AJAX вызов 3 со значением
'1'
Во время вызова AAJX 3, вызов AJAX 2 возвращается со значением '12'
...
TL; DR возникает бесконечный цикл, несмотря на проверку последнего состояния в componentDidUpdate
, поскольку два перекрывающихся вызова AJAX дают чередующиеся значения setState
.
Подход 2
Чтобы решить эту проблему, мой второй подход упрощает систему и делает вызов AJAX непосредственно из обработчика событий:
handleChange(event) {
$.ajax({
url: serviceUrl,
data: JSON.stringify({ inputText: inputTextValue })
// set other AJAX options
}).done((response) => {
setState({ inputText: response.validatedInputTextValue });
});
}
Однако, если я сделаю это, немедленное обновление значения контролируемого компонента будет остановлено до завершения вызова AJAX и вызова setState
. Это просто и стабильно, только установка состояния и рендеринг один раз; но задержка ввода во время ожидания вызова AJAX - плохой UX. Первый подход, по крайней мере, имеет некоторое подобие (чрезмерно) немедленного обновления.
Подход 3?
Пока я жду ответа, я собираюсь реализовать следующий подход 3, который в основном является улучшенной версией подхода 1:
- Добавление идентификатора запроса к данным вызова AJAX, который увеличивается каждый раз при выполнении вызова
- Вывод идентификатора запроса обратно в ответ
- В обратном вызове, если текущий идентификатор запроса больше, чем у ответа, у ответа истекли данные
- Если данные ответа не истекли, звоните
setState
Вопрос
Я все еще относительно новичок в Реакте. Я предполагаю, что кто-то еще сталкивался с этим вариантом использования, но у меня возникли проблемы с поиском решения. Мне нужен способ установить состояние и немедленно обновить значение компонента, а-ля Подход 1, и при этом иметь стабильность данных Подхода 2. Подход 3 кажется многообещающим, но слишком сложным. Есть ли элегантный рисунок, который это выполняет?