Простая машина состояний для VueJS

в 12:35, , рубрики: components, javascript, patterns, state machine, vue.js, vuejs

Простая машина состояний для VueJS - 1

Недавно я наткнулся на интересное обсуждение на Full Stack Radio — Bulding Better UI Components with State Machines. Речь шла о том, что концепция машины состояний может помочь при разработке компонентов Vue. Я начал просматривать готовые решения, но они оказались не столь просты, и мне захотелось сделать более простую реализацию, о которой я и хочу рассказать в этой статье. Статья может оказаться полезной не только для тех кто использует Vue, но и для пользователей Angular, React и т.д., а также для программистов других «конфессий».

В обсуждении затрагивалась тема, близкая всем, кто писал компоненты. Представьте. что вы пишете компонент, который обращается за данными, и вам надо отобразить спиннер на то время, пока запрос выполняется. Обычно в таких случаях вы создаете логическую переменную isLoading. Изначально isLoading = false, а перед тем, как запросить данные вы присваиваете переменной isLoading значение true. После того, как данные пришли вы опять присваиваете ей значение false. Видимость спиннера привязана к isLoading.

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

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

На эту тему довольно много статей (например Finite State Machines in Vue, но большинство из них после краткой теории рекомендуют подключить одну из готовых библиотек. Например davidkpiano/xstate.

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

Вот тут код с которым можно поиграть — State machine with Vue, version 2. А тут сырая версия, со слегка отличающимся подходом — State machine with Vue, version 1.
Отталкивался я от статьи State Machines.

Итак машина состояний:

class StateMachine {
    constructor (initialState, transitions) {
        this.state = initialState
        this.transitions = transitions
    }
    transition (nextState, method, params) {
  	const transitionsArray = this.transitions[this.state]	
        if(transitionsArray.indexOf(nextState) === -1) return this.state
        if(method) method(...params)
        this.state = nextState
        return this.state
    }
}

Инициализируем ее так:

const machine = new StateMachine('idle', {
    idle: ['waitingConfirmation'],
    waitingConfirmation: ['idle','waitingData'],
    waitingData: ['dataReady', 'dataProblem'],
    dataReady: ['waitingConfirmation'],
    dataProblem: ['waitingConfirmation']
})

В методах компонента создаем функцию перехода:

transition(nextState, method = null, params = []) {
    this.machineState = machine.transition(nextState, method, params)
}

В событиях сразу пишем куда хотим перейти:

@click=“transition('waitingConfirmation')"

Если надо при этом вызвать метод компонента пишем так:

@click="transition('waitingData', getData, [222])”

Второй параметр — метод компонента который надо вызвать с этим переходом.
Третий параметр — массив переменных для этого метода.

Внутри методов вызов такой:

this.transition('dataReady')

Я специально не стал скрывать кнопки с помощью v-if, просто пока делаю цвет их шрифта сереньким. Зато видно, что нажатие на них не срабатывает, если машина не разрешает.

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

Автор: Almatyn

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js