Необходимость в дизайн-системе

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

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

Состояние мечты

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

Это означает, что забудьте о рендеринге реквизита и компонентов более высокого порядка. Мы не хотим иметь дело с беспорядочным деревом React и оболочками. Нам не нужны создатели действий, редукторы * или что-то в этом роде.

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

Доступно во всем мире

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

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

Контекст может позволить нам сделать именно это.

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

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

Однако это еще не идеально. Наш провайдер предоставляет мутатор состояния setAlerts напрямую, но нам нужно что-то гораздо более высокого уровня. Мы не хотим, чтобы наши компоненты возились с тем, как добавить оповещение, не разрушая существующие, и так далее. Думайте о setAlerts как о частном методе, и нам нужно предоставить открытый метод, который абстрагирует шум.

API более высокого уровня и поведение

Сейчас происходит гораздо больше. Наш провайдер теперь предоставляет новую функцию под названием addAlert. Эта функция использует мутатор локального состояния useState для правильного добавления нового предупреждения без уничтожения существующих. Здесь вы могли бы делать более интересные вещи: устранять дублирование предупреждений, назначать предупреждениям уникальные идентификаторы, чтобы мы также могли предоставить removeAlert функцию, которая использует эти идентификаторы, и так далее. Но мы не будем усложнять задачу.

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

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

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

Этот процесс рендеринга и побочных эффектов повторяется до тех пор, пока alerts / activeAlertIds не станет пустым. Это наш базовый случай, если вы хотите думать об этом рекурсивно.

Добавляем оптимизацию

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

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

Мы можем использовать useMemo, чтобы запоминать value. Нет причин, по которым этот объект нужно создавать заново каждый раз. Мы передадим ему addAlert как зависимость, т.е. кеш мемоизации необходимо повторно заполнить, если addAlert изменяется.

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

Решение простое и похожее. Мы хотим запомнить функцию так же, как мы это сделали со значением. Единственное отличие состоит в том, что теперь мы не запоминаем результат, а запоминаем функцию, которая даст какой-то результат в будущем (обратный вызов). Для этого мы можем использовать useCallback

Теперь addAlert стабилен между отрисовками: он всегда будет поддерживать ссылочное равенство. В результате alert тоже будет стабильным.

Обратите внимание, что у нас нет зависимостей для этой мемоизации. Мы просто передаем [], что означает «запоминать один раз и всегда возвращать ту же функцию в будущем». Эта функция не зависит ни от чего, кроме локального состояния alerts, но нам не нужно указывать ее в списке зависимостей, потому что мутатор setAlert предоставляет ее нам напрямую.

Возможности для перемен

Много говорят о том, как можно заменить многое из того, что делает Redux, на useContext и useReducer. Комбинация этих двух хуков означает, что мы можем иметь глобальное состояние и использовать Redux-подобные редукторы, действия и диспетчеры для изменения этого глобального состояния. В некоторой степени это возможно.

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