XState简介

I wrote about finite state machines in the past and I mentioned XState. In this post I want to introduce this popular JavaScript library.

我过去写过关于有限状态机的文章 ,并提到XState 。 在这篇文章中,我想介绍这个流行JavaScript库。

Finite state machines are an interesting way to tackle complex state and state changes and keep your code bugs-free as much as possible.

有限状态机是一种有趣的方式,可以解决复杂的状态和状态更改,并尽可能避免代码错误。

Just as we model a software projects using various tools to help us design it before building it, and we use mockups and UX tools to think about an UI before building it, finite state machines help us solve state transitions.

正如我们使用各种工具为软件项目建模以帮助我们在构建软件之前对其进行设计,以及使用模型和UX工具在构建UI之前考虑UI一样,有限状态机可以帮助我们解决状态转换。

Computer programs are all about transitioning from one state to another after an input. Things can get out of control if you’re not paying close attention, and XState is a very helpful tool to help us navigate the state complexity as it grows.

计算机程序都是关于输入后从一种状态过渡到另一种状态的。 如果您不密切注意,事情可能会失控,并且XState是一个非常有用的工具,可帮助我们应对状态复杂性的增长。

You install XState using npm:

您使用npm安装XState:

npm install xstate

then you can import it in your program using the ES Modules syntax. As a minimum you typically import the Machine and interpret functions:

然后您可以使用ES Modules语法将其导入程序中。 通常,至少需要导入Machineinterpret功能:

import { Machine, interpret } from 'xstate'

In the browser you can also import it from a CDN directly:

在浏览器中,您也可以直接从CDN导入它:

<script src="https://unpkg.com/xstate@4/dist/xstate.js"></script>

and this will make a global XState variable on the window object.

这将在window对象上创建一个全局XState变量。

Next you can define a finite state machine using the Machine factory function. This function accepts a configuration object, and returns a reference to the newly created state machine:

接下来,您可以使用Machine factory功能定义一个有限状态机。 此函数接受配置对象,并返回对新创建的状态机的引用:

const machine = Machine({

})

In the configuration we pass an id string that identifies the state machine, the initial state string. Here is a simple traffic lights example:

在配置中,我们传递一个标识状态机的id字符串,即初始状态字符串。 这是一个简单的交通灯示例:

const machine = Machine({
  id: 'trafficlights',
  initial: 'green'
})

We also pass a states object containing the allowed states:

我们还传递了一个包含允许状态的states对象:

const machine = Machine({
  id: 'trafficlights',
  initial: 'green',
  states: {
    green: {

    },
    yellow: {

    },
    red: {

    }
  }
})

Here I defined 3 states: green yellow and red.

在这里,我定义了3种状态: green yellowred

To transition from one state to another we will send a message to the machine, and it will know what to do based on the configuration we set.

为了从一种状态过渡到另一种状态,我们将向计算机发送一条消息,并且它将根据我们设置的配置知道要做什么。

Here we set to switch to the yellow state when we’re in the green state and we get a TIMER event:

在这里,我们设置为在green状态时切换到yellow状态,并得到TIMER事件:

const machine = Machine({
  id: 'trafficlights',
  initial: 'green',
  states: {
    green: {
      on: {
        TIMER: 'yellow'
      }
    },
    yellow: {

    },
    red: {

    }
  }
})

I called it TIMER because traffic lights usually have a simple timer that changes the lights state every X seconds.

我将其称为TIMER是因为交通信号灯通常具有一个简单的计时器,该计时器每X秒更改一次灯光状态。

Now let’s fill the other 2 state transitions: we go from yellow to red, and from red to green:

现在让我们填充其他两个状态转换:我们从黄色到红色,从红色到绿色:

const machine = Machine({
  id: 'trafficlights',
  initial: 'green',
  states: {
    green: {
      on: {
        TIMER: 'yellow'
      }
    },
    yellow: {
      on: {
        TIMER: 'red'
      }
    },
    red: {
      on: {
        TIMER: 'green'
      }
    }
  }
})

How do we trigger a transition?

我们如何触发过渡?

You can get the initial state string representation of the machine using:

您可以使用以下命令获取机器的初始状态字符串表示形式:

machine.initialState.value //'green' in our case

and we can switch to a new state using the transition() method of machine (the state machine instance returned by Machine()):

我们可以使用切换到新的状态transition()的方法machine (由返回的状态机实例Machine()

const currentState = machine.initialState.value
const newState = machine.transition(currentState, 'TIMER')

You can store the new state object into a variable, and you can get its string representation accessing the value property:

您可以将新的状态对象存储到变量中,并可以通过访问value属性来获取其字符串表示形式:

const currentState = machine.initialState.value
const newState = machine.transition(currentState, 'TIMER')
console.log(newState.value)

Using the transition() method you always have to keep track of the current state, which in my mind causes a bit of pain. It’d be great if we could ask the machine for its current state.

使用transition()方法,您始终必须跟踪当前状态,在我看来这会带来一些麻烦。 如果我们可以询问机器当前状态,那就太好了。

This is done by creating a statechart, which in XState is called service. We do so calling the interpret() method we imported from xstate passing it the state machine object, and then calling start() to start the service:

这是通过创建状态图完成的,该状态图在XState中称为服务。 xstate ,我们调用从xstate导入的interpret()方法, xstate传递给状态机对象,然后调用start()来启动服务:

const toggleService = interpret(machine).start()

Now we can use this service send() method to retrieve the new state, without having to pass the current state like we have to do with machine.transition():

现在,我们可以使用此服务的send()方法来检索新状态,而不必像使用machine.transition()那样传递当前状态:

const toggleService = interpret(machine).start()
toggleService.send('TOGGLE')

We can store the return value, that will hold the new state:

我们可以存储返回值,该值将保持新状态:

const newState = toggleService.send('TOGGLE')
console.log(newState.value)

This is just scratching the surface of XState.

这仅仅是XState的表面。

Given a state, you can know what will trigger a state change using its nextEvents property, that will return an array.

给定一个状态,您可以使用其nextEvents属性知道什么将触发状态更改,该属性将返回一个数组。

Yes, because from a state you can go to multiple states depending on the trigger you get.

是的,因为您可以根据触发条件从一个状态进入多个状态。

In the case of traffic lights, this is not something that will happen, but let’s model the house lights example we had in the finite state machines post:

对于交通信号灯,这不会发生,但是让我们对有限状态机中的房屋信号灯示例进行建模:

When you enter the house, you can press one of the 2 push buttons you have, p1 or p2. When you press any of those buttons, the l1 light turns on.

进入房屋时,您可以按两个按钮之一p1或p2。 当您按这些按钮中的任何一个时,l1指示灯将点亮。

Imagine this is the entrance light, and you can take your jacket off. Once you are done, you decide which room you want to go into (kitchen or bedroom, for example).

想象这是入口灯,您可以脱下外套。 完成后,您可以决定要进入哪个房间(例如厨房或卧室)。

If you press the button p1, l1 turns off and l2 turns on. Instead if you press the button p2, l1 turns off and l3 turns on.

如果按按钮p1,则l1关闭,l2打开。 相反,如果您按下按钮p2,则l1关闭,l3打开。

Pressing another time any of the 2 buttons, p1 or p2, the light that is currently on will turn off, and we’ll get back at the initial state of the system.

再次按下两个按钮p1或p2中的任何一个,当前点亮的灯将熄灭,我们将回到系统的初始状态。

Here is our XState machine object:

这是我们的XState机器对象:

const machine = Machine({
  id: 'roomlights',
  initial: 'nolights',
  states: {
    nolights: {
      on: {
        p1: 'l1',
        p2: 'l1'
      }
    },
    l1: {
      on: {
        p1: 'l2',
        p2: 'l3'
      }
    },
    l2: {
      on: {
        p1: 'nolights',
        p2: 'nolights'
      }
    },
    l3: {
      on: {
        p1: 'nolights',
        p2: 'nolights'
      }
    },
  }
})

Now we can create a service and send it messages:

现在我们可以创建服务并向其发送消息:

const toggleService = interpret(machine).start();
toggleService.send('p1').value //'l1'
toggleService.send('p1').value //'l2'
toggleService.send('p1').value //'nolights'

One thing we miss here is how do we do something when we switch to a new state. That is done through actions, which we define in a second object parameter we pass to the Machine() factory function:

我们在这里想念的一件事是,当我们切换到新状态时我们该如何做 。 这是通过操作完成的,我们在第二个对象参数中定义了这些操作,然后将其传递给Machine()工厂函数:

const machine = Machine({
  id: 'roomlights',
  initial: 'nolights',
  states: {
    nolights: {
      on: {
        p1: {
          target: 'l1',
          actions: 'turnOnL1'
        },
        p2: {
          target: 'l1',
          actions: 'turnOnL1'
        }
      }
    },
    l1: {
      on: {
        p1: {
          target: 'l2',
          actions: 'turnOnL2'
        },
        p2: {
          target: 'l3',
          actions: 'turnOnL3'
        }
      }
    },
    l2: {
      on: {
        p1: {
          target: 'nolights',
          actions: ['turnOffAll']
        },
        p2: {
          target: 'nolights',
          actions: ['turnOffAll']
        }
      }
    },
    l3: {
      on: {
        p1: {
          target: 'nolights',
          actions: 'turnOffAll'
        },
        p2: {
          target: 'nolights',
          actions: 'turnOffAll'
        }
      }
    },
  }
}, {
  actions: {
    turnOnL1: (context, event) => {
      console.log('turnOnL1')
    },
    turnOnL2: (context, event) => {
      console.log('turnOnL2')
    },
    turnOnL3: (context, event) => {
      console.log('turnOnL3')
    },
    turnOffAll: (context, event) => {
      console.log('turnOffAll')
    }
  }
})

See how now each state transition defined in the object passed to on instead of being just a string it’s an object with the target property (where we pass the string we used before) and we also have an actions property where we can set the action to run.

看看现在如何将对象中定义的每个状态转换传递给对象on而不仅仅是一个字符串,它是一个具有target属性的对象(我们传递之前使用的字符串),还有一个actions属性,可以将action设置为跑。

We can run multiple actions by passing an array of strings instead of a string.

我们可以通过传递字符串数组而不是字符串来运行多个动作。

And you can also define the action(s) directy on the actions property instead of “centralizing” them into a separate object:

您还可以直接在actions属性上定义动作,而不是将它们“集中”到一个单独的对象中:

const machine = Machine({
  id: 'roomlights',
  initial: 'nolights',
  states: {
    nolights: {
      on: {
        p1: {
          target: 'l1',
          actions: (context, event) => {
            console.log('turnOnL1')
          },
          ...

But in this case it’s handy to put them all together because similar actions are fired by different state transitions.

但是在这种情况下,将它们放在一起很方便,因为不同的状态转换会触发相似的动作。

That’s it for this tutorial. I recommend you to check out the XState Docs for more advanced usage of XState, but it’s a start.

本教程就是这样。 我建议您查看XState Docs以获取XState的更高级用法,但这只是一个开始。

翻译自: https://flaviocopes.com/xstate/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值