React: Должна ли установка состояния внутри дискретных событий вызывать запуск очистки?

Созданный на 5 мар. 2019  ·  3Комментарии  ·  Источник: facebook/react

Эта ошибка довольно сбивает с толку:

https://twitter.com/kentcdodds/status/1102659818660102145

Я думаю, это происходит потому, что fn запланированный setInterval(fn, 0) перескакивает перед очисткой эффекта [running] вызванного setRunning(false) . Таким образом, интервал все еще срабатывает, перезаписывая setLapse(0) , произошедшее во время события, его setLapse(someValue) .

Это напоминает мне проблему, описанную в https://github.com/facebook/react/issues/14750#issuecomment -460409609, или, по крайней мере, ее часть:

Фактически, эта проблема существует даже для обычных нажатий клавиш React (и других «дискретных» событий). Решением этого будет сброс пассивных эффектов до того, как мы получим дискретное событие.

Но здесь кажется, что этого недостаточно, потому что эффект переворачивается в результате щелчка, а не до него. Так должен ли setState внутри дискретного события также сбрасывать пассивный эффект? Похоже, что нет. (Это лишило бы смысла их задерживать.)

Итак, это работает, как задумано, и исправление составляет всего useLayoutEffect когда время имеет значение? Или решение rAF?

Hooks Question

Самый полезный комментарий

Один из подходов состоит в том, чтобы отправить сброс только после того, как таймер действительно остановится, то есть в результате. Если вы хотите, чтобы он всегда отображал ноль в одном и том же кадре, вы всегда можете отображать ноль, когда выполнение ложно.

Однако, как почти всегда, это лучше смоделировать как редуктор. Редуктор может легко отказаться от обновления обновлений, если он думает, что он остановлен, а также выполнить логику для его фактического сброса.

Все 3 Комментарий

Проблема в том, что сам таймер не является дискретным событием. Дискретные события гарантируются только в отношении других дискретных событий. Я не думаю, что это то, что вам здесь нужно.

Остановка таймера является асинхронной операцией, поскольку все установленные состояния являются асинхронными. Так что что-то еще может войти, прежде чем оно будет смыто.

Это тот случай, когда useLayoutEffect на самом деле не решает проблему полностью. Конечно, не в параллельном режиме, но в режиме синхронизации это тоже отрывочно. Если бы это был не таймер, а, скажем, событие фокуса, оно могло бы срабатывать в пакете до того, как оно будет сброшено, что будет иметь ту же проблему.

setRunning(false); // I would like to add a stop of this timer to the queue to be performed later
setLapse(0); // I would like to add an operation to set lapse to zero later
// lots of random stuff that can happen before the batch flushes
// This might also queue an operation to set lapse to something else
// actual rendering
// If concurrent mode, lots of other random stuff that can happen while rendering
// This might also queue an operation to set lapse to something else
// Actually do all that work in order

Все дело в том, в каком порядке вещи добавляются в очередь.

Один из подходов состоит в том, чтобы отправить сброс только после того, как таймер действительно остановится, то есть в результате. Если вы хотите, чтобы он всегда отображал ноль в одном и том же кадре, вы всегда можете отображать ноль, когда выполнение ложно.

Однако, как почти всегда, это лучше смоделировать как редуктор. Редуктор может легко отказаться от обновления обновлений, если он думает, что он остановлен, а также выполнить логику для его фактического сброса.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги