Как написать реагирующие эффекты с неявной очисткой.

Уборка утомительна

Запустить тайм-аут или добавить прослушиватель событий при монтировании компонента достаточно просто, используя хук useEffect реакции.

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

Для этого мы возвращаем из хука функцию, которая занимается очисткой:

К сожалению, это означает, что удобочитаемость снижается, поскольку то, что мы на самом деле хотим сделать, начинает скрываться в том, что мы должны сделать.

Это становится еще хуже для прослушивателей событий:

Нам нужно присвоить наш обратный вызов переменной, чтобы мы могли передать его и в addEventListener, и затем в removeEventListener в функции очистки. И window, и тип события ('click') дублируются. 🤨

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

Теперь, как мы можем улучшить это?

На самом деле нас не волнует timeoutId или ссылка на обратный вызов события. Все, что мы хотим, это добавить его при монтировании и отменить/удалить при размонтировании.

В идеале эффект должен просто «знать», как удалить себя при размонтировании компонента.

Давайте создадим функцию-оболочку, которая делает именно это:

Вместо того, чтобы возвращать идентификатор, эта функция удобно возвращает обратный вызов для очистки тайм-аута. Ссылка на идентификатор находится внутри него, единственное место, где он нам действительно нужен.

Теперь мы можем радикально упростить наш useEffect:

Вау, мы сократили его до одной строчки! Не плохо, верно? Давайте разберем это:

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

Мы успешно сократили код нашего примера и улучшили его, чтобы показать только наши намерения, сделав всю логику очистки неявной.

А события?

Хороший вопрос! Они работают точно так же!

Сначала мы создадим функцию-оболочку:

Теперь, чтобы добиться того же, что и раньше, мы можем написать:

Мы объединили 9 строк в одну, избежали повторений, сделали наш код более разборчивым и, таким образом, наши намерения более понятными.

Можем ли мы сделать еще лучше?

Конечно! Вы можете добавить больше функций, таких как setInterval , добавить все функциональные возможности оригиналов (например, параметры прослушивателя событий или дополнительные параметры, которые будут переданы в обратный вызов setTimeout), заставить их работать как в среде браузера, так и в среде узла и наиболее полезны из всех:
добавить правильные типы машинописных текстов.

Но поскольку я уже сделал все это, вы можете избавить себя от некоторых проблем и просто использовать пакет набора функций, который я опубликовал на NPM: https://www.npmjs.com/package/reversible-effect

Ждем отзывов и вопросов.

Удачного кодирования! :)