Просто хотите код? В этом репо показано, как интегрировать xstate с React Context + Hooks. Однако здесь нет invoke кода, поэтому его стоит прочитать!



Некоторый контекст

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

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

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

Я хочу поговорить о том, что было бы во второй части этой серии, - это свойство invoke, которое было введено в xstate со времени написания первой части, потому что invoke-ing - это гораздо больше мощнее, чем функция генератора, которую я использовал ранее. Это идиоматично. Он мыслит государственной машиной.

Это безумно хорошо.

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

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

Но сначала позвольте мне перейти в режим рекламы, потому что, хотя раньше я любил конечные автоматы, со свойством invoke я чувствую, что обладаю «суперсилой», когда дело касается того, как я теперь моделирую состояние приложения. Я использовал Recompose, Redux, Observables, MobX, «обычный» Context + Hooks - почти все, что есть, и, по крайней мере, для меня диаграммы состояний не только самые эффективные, но и самые забавные.

Да, мне нравится писать диаграммы состояний. Перестань так на меня смотреть.

Хорошо, хватит шумихи, давайте напишем код!

Ознакомления

Xstate docs действительно хороши, поэтому я не очень хочу здесь воссоздавать колесо (и я предполагаю, что у вас есть хотя бы беглое представление о машинах состояний), но что я сделаю, так это скопирую подмножество API вызова, с примером из документации:

API

  • src
  • onDone
  • onError
  • данные

Пример

Итак, здесь мы видим фрактальную природу того, чем выделяются диаграммы состояний: клавиша invoke на loading может быть обещанием или другой диаграммой состояний, содержащей множество других обещаний.

Это супер мощно.

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

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

Предполагая, что обещание / диаграмма не выдает ошибку, когда someSrc завершает , он может перейти к loadBar, который может выполнить дополнительную асинхронную работу со своим собственным invoke, прежде чем окончательно переместиться в loadingComplete .

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

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

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

Правило большого пальца

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

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

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

Тестирование

API xstate работает следующим образом: ключ invoke может принимать прямую ссылку на диаграмму обещания / состояния или может принимать строковую ссылку, которая просматривается в ключе services в объекте параметров.

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

Ключевым моментом здесь является то, что мы расширяем машинные сервисы, используя withConfig, чтобы изменить getUser на нашу фиктивную функцию тестирования.

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

Также существует пакет @ xstate / test со стратегией, позволяющей помещать информацию о тестировании в test ключ на диаграмме состояний. Я еще не использовал это, так что я не могу говорить с ним, но это выглядит интересно.

Работа с React

Хорошо, вот базовый компонент React для загрузки данных. Пользовательский интерфейс более или менее зависит от состояния, как и должно быть в React. Обратите внимание, насколько это близко к готовящемуся к выпуску Suspense API.

Здесь следует отметить несколько моментов:

  • Контекст - это, по сути, состояние для xstate, что сбивает с толку, но да, именно здесь вы отслеживаете состояние машины. Полученные данные, вычисленные данные и тому подобное.
  • Send аналогичен Redux dispatch, где вы запускаете строковое действие, которое соответствует действию на диаграмме состояний, а ловушка React интерпретирует изменение состояния, распространяя повторную визуализацию.

ИМО, держите как можно большую часть своего состояния в машинах, чтобы при необходимости вы могли относительно легко переключать фреймворки рендеринга. Хук @ xstate / response можно легко перепрофилировать для работы с другими фреймворками.

Об использовании ловушек для состояния

В предыдущем приложении, которое использовало xstate, я работал, мы определенно использовали нашу долю платы за проезд в размере useState. Крючки легкие и удобны при правильном использовании. Иногда для перфоманса или в ином случае вам нужно просто использовать крючки. Хорошо.

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

Последние мысли

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

Вот в чем дело. Эта история еще не закончена.

Поскольку Node и IoT в основном управляются Javascript, это означает, что если вы изучаете xstate (или любую другую подобную библиотеку), вы можете моделировать свои состояния где угодно.

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