Когда я пишу приложения, я хочу проводить как можно больше времени, делая три вещи:

  1. Определите состояние и области этого состояния
  2. Напишите логику для изменения состояния
  3. Использовать состояние в компонентах для создания пользовательского интерфейса для пользователей

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

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

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

Заявление об ограничении ответственности. Я буду отрицательно ссылаться на существующие инструменты. Это не означает, что я не уважаю авторов или считаю, что инструменты не имеют ценности. Напротив, инструменты, упомянутые в этой статье, были очень важны для сообщества в целом, а также для моего личного обучения и продуктивности.

Что теперь делать?

Для создания приложения у нас есть три основных ингредиента. Состояние, логика для изменения состояния и пользовательский интерфейс, который создается путем использования состояния.

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

Зуд

Машинопись

Все, кто знает Typescript, также знают, что адаптироваться к нему довольно болезненно. Однако не всегда указывается, что это не имеет ничего общего с самим Typescript, это связано с использованием инструментов, которые никогда не предназначались для использования с Typescript. Javascript - это динамический язык, который позволяет много творческого подхода к определению API, API, которые Typescript просто не может выразить, или типы вообще не реализованы. Мне особенно больно было с библиотеками компонентов Redux и React. В Redux столько же подходов к типизации, сколько и приложений, а библиотеки компонентов не определяют правильную типизацию своих компонентов высшего порядка, вызывая всевозможные странные ошибки при попытке скомпоновать компонент. Это не значит, что Redux или библиотеки компонентов плохие, это просто досадное последствие, которое создает трения при разработке приложений.

Архитектура потока

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

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

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

Подключение зависимостей

Здесь я немного поругаюсь с Redux. Не потому, что я считаю библиотеку плохой, а потому, что она заняла позицию «святого Грааля» государственного управления, несмотря на свою высокую стоимость. Об одной стоимости мы уже упоминали выше, но другая стоимость связана с подключением состояния и действий к компонентам.

Для каждого компонента, которому требуется доступ к внешнему состоянию и / или действиям, необходимо явно определить функцию, которая извлекает все, что вы хотите предоставить компоненту. Само по себе это всего лишь дополнительный синтаксис. Там, где это становится издержками, так это то, что вам нужно оценить, какой уровень вложенности состояния вы можете выставить в зависимости от того, как часто компонент должен отображаться. И все это возвращается к пониманию концепции проверки ссылок (неизменяемости). Если вы предоставите компоненту состояние верхнего уровня, компонент будет отображаться при любом изменении состояния. Если вы открываете вложенный словарь сообщений, компонент будет отображать любое изменение в любом сообщении. В определенных ситуациях (а такие ситуации всегда возникают в любом приложении среднего размера) необходимо использовать shouldComponentUpdate. Это трение и снижает опыт разработчика. Определение этих коннекторов и shouldComponentUpdate не поможет вам создать приложение, в первую очередь вы поможете Redux и React избежать ненужного рендеринга.

Императив против функционального

Всегда будут дискуссии о преимуществах императивного и функционального программирования. Для меня императивное программирование более прямолинейно, это больше как мой мозг думает о вещах. Я приказываю компьютеру внести изменения в мое состояние, я не уменьшаю или просматриваю состояние. Но я не об этом. Я хочу сказать, что мы, кажется, выбираем одно или другое. Если вы выберете RxJS, все должно быть RxJS. Или, если вы выберете redux-sagas или redux-loop, все должно быть определено таким образом. Я считаю, что это неправильно. Простые изменения состояния должны выполняться в обязательном порядке, поскольку это сокращает необходимый синтаксис и концепции вокруг него. Сложные изменения состояния должны выполняться функционально, поскольку это сокращает необходимый синтаксис и концепции вокруг него. Позвольте мне объяснить на примере.

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

state.stateToToggle = !state.stateToToggle

Но тогда у нас есть кое-что более сложное. Мы хотим произвести поиск. Этот поиск должен выполняться только в том случае, если запрос имеет определенную длину и только когда пользователь перестал печатать на 200 мсек. Это было бы «полный беспорядок», реализуемый императивно, но функционально вы могли бы сказать что-то вроде:

export const search = pipe(
  setQuery,
  filterValidQuery,
  debounce(200),
  getSearchResult
)

Я считаю идеальным наличие API, позволяющего перемещаться между этими двумя мирами, не привязываясь к одному или другому.

Буровая установка и мероприятия

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

Компонент и состояние приложения

Первой причиной, по которой я занялся открытыми исходными кодами инструментов управления состоянием, было сильное желание создавать сложные пользовательские интерфейсы. Когда бы я ни определял состояние, оно в некотором смысле было недоступно. Причина заключалась в том, что он был определен в представлении / компоненте. Разрабатывая свои приложения, я постоянно реорганизовывал то, где это состояние было определено, чтобы получить доступ к любым представлениям / компонентам, которым требовался доступ к нему. Решением было переместить это «состояние приложения» за пределы уровня представления / компонента. Это не значит, что состояние компонента бесполезно, но, по моему опыту, у вас есть лучший опыт разработчика, рассматривающий ваше состояние в первую очередь как «состояние приложения», потому что тогда по умолчанию оно всегда будет доступно для всех, в чем оно нуждается, без каких-либо дополнительных свойств или рефакторинга. состояние на другой вид / компонент. Во-вторых, вы используете изолированное «состояние компонента» там, где это имеет смысл.

Почесывая зуд

Все вышеупомянутые проблемы я называю «трением». То, что я должен делать как разработчик, меня раздражает, нарушает мой рабочий процесс или я просто считаю совершенно ненужным. Проект, о котором я собираюсь вам рассказать, сводит все вышеупомянутые трудности к минимуму, делая разработку приложений более приятной, не забывая о важности предсказуемости, ремонтопригодности и масштабируемости.

Я хочу начать с признательности Мишелю Вестстрату и его проекту Mobx. Этот проект дал столь необходимый противовес Redux и неизменяемости. Это также сильно вдохновило меня и мою группу открытых волшебников на то, чтобы действительно усердно работать над выбором API и необходимой базовой реализации, чтобы снизить трение при разработке приложений.

Проект так и не отказался от рабочего названия и теперь носит официальное название Overmind. Проект находился в разработке более 6 месяцев и не мог увидеть свет раньше. Причина в том, что мы сильно зависим от последних выпусков Typescript, а также от добавления прокси во все современные браузеры.

Давайте теперь посмотрим, как этот проект уменьшает трение при разработке приложений, возвращаясь к этому зуду. Чтобы получить дополнительную информацию об API и его работе, посетите официальный сайт: https://www.overmindjs.org.

Определение состояния

Overmind можно использовать с любыми популярными фреймворками. Это Vue JS, Angular или React. Когда вы вводите Сверхразум в свой проект, рекомендуется в первую очередь определять свое состояние в Сверхразуме и определять его как дерево состояний. Это означает, что вам не нужны никакие классы или редукторы, вы просто создаете простые объекты, которые описывают состояние вашего приложения. Вы можете масштабировать свое состояние по разным доменам, даже лениво загружать состояние, и в конце концов все это объединяется в одно дерево состояний.

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

Изменение состояния

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

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

Когда ваше состояние становится сложным, у вас появляется естественная ступенька к функциональному API, предоставляемому Overmind. Вы даже можете повторно использовать существующие действия и скомпоновать их с помощью функциональных операторов, таких как фильтр, карта, противодействие и т. Д. Это позволяет вам мыслить простыми императивными терминами для простой логики, а затем идти функционал там, где он действительно дает ценность. (Приведенный выше пример поиска на самом деле является API Overmind).

Состояние потребления

Поскольку Overmind основан на прокси-серверах, он точно знает, к какому состоянию обращаются ваши компоненты. Это означает, что нет причин сопоставлять состояние и действия с реквизитами. Вы просто используете состояние напрямую, и все, что используется, отслеживается. Это означает, что Overmind оптимизирует рендеринг компонентов и делает это намного лучше, чем если бы вы когда-либо делали сопоставление состояния вручную. Хотя все это происходит неявно, это не выходит из-под вашего контроля. Инструмент разработки Overmind, который представляет собой отдельное приложение, предоставляет всю необходимую информацию. Какое состояние определяется, какие действия выполняются, что они делают и какие компоненты зависят от того, в каком состоянии.

Мне часто задают вопрос: «Но мне нравится явно объявлять свойства, которые использует мой компонент». Моя теория заключается в том, что это утешительный аргумент, поскольку он помогает вам определить, почему ваш компонент отображается, я должен знать ... Я использовал этот аргумент много раз. Но теперь вы снова переживаете, когда думаете об этом, нет никакого трения «будет ли мой компонент визуализировать слишком много».

Машинопись

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

Но самое главное, что Overmind написан на Typescript с нуля с акцентом на типы, являющиеся частью реального API. Это подбрасывает монету. TypeScript всегда помогает вам в Overmind, это никогда не будет рутиной, которую он слишком часто испытывает с инструментами, написанными не на Typescript.

Резюме

Может, эта статья вас только спровоцировала. Возможно, вам очень нравится решение для управления состоянием, которое вы используете сегодня, и оно прекрасно решает задачу создания вашего приложения. Повезло тебе! Важно найти инструменты, которые сделают вас и вашу команду продуктивными и счастливыми разработчиками. Я только чесал свой собственный зуд, и оказалось, что другие разработчики разделяют этот зуд, и именно поэтому существует Overmind. Я надеюсь, что, по крайней мере, этот проект внес свой вклад в обсуждение и вопросы о том, чего вы ожидаете от инструментов, которые вы используете, и, возможно, он даже поцарапал ваш зуд, если это так, перейдите на https://www.overmindjs.org и узнайте больше о проекте!

📝 Прочтите этот рассказ позже в Журнале.

🗞 Просыпайтесь каждое воскресное утро и слышите самые интересные истории, мнения и новости недели, ожидающие в вашем почтовом ящике: Получите примечательный информационный бюллетень›