Писать кнопочки и формочки на React — дело не хитрое. Но почти всегда фронтовые проекты превращаются в нечто трудночитаемое и едва ли поддерживаемое. Визуально различия проектов на React и JQuery со временем сохраняются, а вот developer experience с точки зрения трудозатрат на поддержку становится примерно одинаковым.
За лесом кнопок, эффектов и пропсов не разглядеть сценария
JavaScript на то и script, что на нём хорошо писать пользовательские сценарии. React — отличная ui-библиотека. Главное не перепутать. Поддержка пользовательских сценариев описанных через ui-библиотеку — для сильных духом.
Привожу пример. Пожалуй самый распространённый случай — это извращенная интерпретация принципа DRY. Теперь поподробнее: речь идёт про случаи, когда имеется чёткий триггер, например, клик по кнопке, ввод текста в инпуте, но сценарий обработки этого триггера находится в useEffect. Почему в useEffect, а не в onClick или onChange? Ну как же, ведь этот же сценарий запускается ещё и при изменении вот того пропса. Не писать же одно и то же дважды.
Мы перестали оперировать понятиями функций и аргументов. Вместо них мы используем эффекты и пропсы
Чем дальше в лес, тем больше дров. Со временем этот useEffect станет «местом силы». Всё больше и больше пользовательских сценариев будут использовать этот хук, массив зависимостей начнёт расти, а внутри хука появятся инструкции if с хитрыми условиями в скобках. Ну и вишенка на торте — понимание, что без хука наподобие usePrevious ничего не заработает. Не всегда usePrevious показатель неправильно выбранного тригера, но в 9 из 10 случаев вам есть о чём задуматься.
Всего два простых правила
-
кратчайший путь для сценария
-
изоляция сценария
Кратчайший путь как раз и начинается с выбора правильного тригера. Например, можно сравнить два варианта из сценария проверки на валидность текста. Есть input, есть стейт value и есть стейт isValid.
Сценарий 1:
-
onChange устанавливает значение value в стейт
-
затем useEffect реагирует на изменение value, проверяет его на валидность и устанавливает в стейт значение isValid
Сценарий 2:
-
onChange вызывает обработчик события с аргументом value, который и установит в стейт value и isValid
Не стоит забывать, что фреймворк — это история про инверсию управления. В данном случае сценарий 2 короче сценария 1 не вдвое, а сильно больше. Ведь во втором случае, при выполнении нашего сценария мы не передавали управление react’у. В первом случае react выполнил один лишний рендер, возможно запустил ещё какие-то хуки, хотя нам это и не надо.
Разрывы в пользовательском сценарии — источник неприятностей
Так мы плавно подходим ко второму пункту — изоляции. Допустим в компонент приходит пропс, который также влияет на значение isValid, тогда для того, чтобы развести два этих сценария достаточно просто описать функцию валидации и передать её в useEffect и в onChange.
Старайтесь писать так, чтобы useEffect был триггером пользовательского сценария, а не его промежуточным звеном
Всё это поможет другим разработчикам не плутать от useEffect’а к useEffect’у по вашему коду, а зацепившись за триггер последовательно «размотать» сценарий.
Обратная сторона
Мы рассмотрели ситуацию, когда логика приложения просачивается в инструменты построения ui. Теперь рассмотрим второй частый случай, когда внутренности ui протекают в модель приложения.
Давайте рассмотрим сценарий побольше, нежели валидация инпута. Например, имеется список запланированных дел (типичная тудушка), у каждого запланированного действия есть кнопка «удалить», которая вызывает попап, который спрашивает «уверены ли вы, что хотите удалить запланированное действие».
О протечке нам просигнализирует появление в сторе (или в верхнеуровневом компоненте) поля, например, popout: { type, data }. Далее в коде можно обнаружить компонент с немаленькой инструкцией switch, который только и ждет, когда в поле popout положат данные. И вот он подбирает нужный компонент (согласно type), передаёт ему data и сценарий продолжается.
Почему такой обрыв нежелателен? Потому что теряется декларативность, а это именно то, почему мы вибирали react изначально. Вручную ставим, вручную подчищаем.
Пока не исчерпаны все возможности данные должны спускаться сверху вниз. И только когда возможности исчерпаны можно отступить
Косательно примера с todo-листом можно поместить компонент popout внутри компонента ячейки с запланированным действием и при нажатиии на кнопку «удалить» показывать popout через портал. Каждая ячейка имеет свой popout, а значит рассказывает максимально короткий и изолированный сценарий. Для простоты можете использовать библиотеку «react-portal» — он прикрепляет содержимое к document.body. Это не плохо подойдёт для всплывающих окон. Для сценариев, где layout сложный и прикрепление компонента к document.body не отвечает потребностям, я написал библиотеку «react-jsx-portal» (ссылка). Вторая часть readme на русском. Я думаю многим она подойдёт.
ссылка на оригинал статьи https://habr.com/ru/post/600035/
Добавить комментарий