谈到状态管理库,有人就会说谁还没用过Redux、Vuex之类的,实际上它们并不是真的状态管理库,本文今天介绍一个真正的状态管理库--XState,一个完全的有限状态机的实现。
包括状态模式和策略模式、状态机以及状态图,可以帮助更好地理解XState。
XState是状态管理工具,应对程序中状态变化切换行为的需求,是一个状态机的实现,理解状态机有必要先理解一下状态模式,说到状态模式不得不提一下策略模式,因为它们太像了,是行为设计模式中的一对亲兄弟,下面是对应的类图:
状态模式和策略模式有一些显著特征便于理解和区别
特征:
策略对应行为,每一个行为都需要一种策略;
策略和对象对应,不能自己更换;
状态决定行为,状态绑定一个或一组行为;
对象可以自己变换状态;
不必过多关注行为,只需要关注状态;
状态模式:
策略模式:
区别:
应用场景更偏向于有明显继承关系但行为复杂的情形,即用组合代替继承;
应用场景更偏向于有明显状态流转的情形,比如红绿灯、开关、ATM机等;
状态模式下,不同的状态由子类实现,通过状态注入Context,可以自行切换;
策略模式下,不同的行为由子类实现,通过行为注入Context,不能自行切换;
联系:
都是行为类设计模式的实现,都是为了处理多种情形的场景,都符合对扩展开放,对修改关闭的原则;
实际上,状态模式可以看成是策略模式的进阶,策略模式更具有一般性;
状态模式的优点:
扩展开放,修改关闭,便于维护和新增状态;
行为逻辑被分散到子类中,Context中的请求动作和状态类不干扰;
状态模式的缺点:
多了很多状态类,增加了开销;
行为逻辑分散到了多个状态类中;
最直接的实现就是针对有限状态机定义封装的状态机函数库,比如JavaScript-State-Machine[1]、Xstate[2]等;
广义的应用还有状态管理相关的各种函数库,一般会把状态管理单独抽取出来维护,并且和行为解耦,但是实际上仍然是状态模式的体现,只不过需要人为的收集所有的状态以及绑定行为,但是实际上仍然是一个状态对应一个行为,比如Redux[3]、Vuex[4]等;
状态机也是对现实世界的抽象,现实世界的大多数场景都可以通过状态机来解释和模拟
状态:{清醒,睡着}
事件:{躺下闭眼,起来睁眼}
动作:执行某个变换
变换:{清醒=>睡着,睡着=>清醒}
比如睡觉这个事情:(比较理想,实际情况可能会细分更多的状态和输入)
延伸
作为一个数学模型,状态机体现的是规则,只要制定好了规则,就可以驱动状态机执行,听起来是一个很计算机的概念,是的,计算机设计中的很多产物都和状态机相关,有兴趣的可以继续了解从有限状态机、图灵机到现代计算机[5];
编译原理中经常会用到状态机的概念,在词法分析阶段根据不同的输入确定不同的行为,诸如此类,在程序设计中十分常见;
触发器也是很明显的状态模式的应用;
Promise;
XState是一个比较标准的有限状态机的实现,并可以通过图形化的方式转换为状态图的形式; 优势
XState是一个状态管理库,Redux、Vuex之类,也被称为状态管理库,但是他们之间的区别还是较大的,这里以Redux为例,与XState做一个简单的比较;
优点
扩展性,模板代码,逻辑集中,对于扩充状态节点和对应的行为比较友好;
通用性,XState定义的状态机和应用有一定程度的解耦,可以切换应用在不同的组件中;
逻辑性,XState定义的状态机对状态和行为都具有约束,状态流转的规则是确定,状态和行为的对应关系也是确定的,因此当前状态的下一个状态及行为都是可预测和观测的;
可视性,确定的状态机就有唯一的状态图,通过状态图可以确认当前应用的逻辑是否符合预期,或者说,可以首先按照需求确定状态图,按照状态图开发,降低风险;
方便使用路径算法进行自动化测试;
缺点
学习成本较高,国内缺乏教程和最佳实践,但是有很好的官方文档(英文);
提前关注逻辑,完成状态图并确保符合预期,这部分投入较高,存在思维转换的过程;
状态和上下文需要分开关注,同样需要转换思维,将状态分离出来;
状态不能无限扩充(本来就是有限状态机),需要适当拆分(XState本身就是可嵌套的过程);
XState定位为一个通用的工具,官方给出了在不同的语言框架中的使用示例,这里从基本的状态机开始,给出一些简单实例;
import { createMachine, interpret } from 'xstate';
const lightMachine = createMachine({
initial:'red',
states: {
red: {
on:{
click:'green',
}
},
green: {
on:{
press:'yellow',
}
},
yellow: {
on:{
keyup:'red',
}
},
},
});
//获取初试状态
const state0 = lightMachine.initialState;
console.log(state0);
//通过transition函数切换状态,第一个参数为原状态,第二个参数为自定义操作
const state1 = lightMachine.transition(state0, 'click');
console.log(state1);
const state2 = lightMachine.transition(state1, 'press');
console.log(state2);
const state3 = lightMachine.transition(state2, 'keyup');
console.log(state3);
const state0 = lightMachine.initialState;
const state1 = lightMachine.transition(state0, 'click');
console.log('state1',state1);
const state2 = lightMachine.transition(state1, 'click');
console.log('state2',state2);
虽然通过machine可以创建状态机,并配合一些api和property可以使用,但是却有很多不方便,最关键的缺少一个管理者的角色,最直接的问题,如何获得当前状态呢?
XState提供了Interpret来创建服务作为状态机的管理者角色
import { createMachine, interpret } from 'xstate';
const lightMachine = createMachine({
initial:'red',
states: {
red: {
on:{
click:'green',
}
},
green: {
on:{
press:'yellow',
}
},
yellow: {
on:{
keyup:'red',
}
},
},
});
//包装服务
const service = interpret(lightMachine);
//启动服务
service.start();
//通过send切换状态,相当于lightMachine.transition函数
service.send('click');
//获取当前状态
console.log('service.state',service.state)
service.send('press');
console.log('service.state',service.state)
console.log('state.value',service.state.value);
console.log('matches green',service.state.matches('green',));
console.log('state.nextEvents',service.state.nextEvents);
service.stop();
这样一个简单的machine好像做不了什么,和Redux相比,基本的上下文数据都没有,别急。。。
import { createMachine, interpret, assign} from 'xstate';
const lightMachine = createMachine({
id:'lightMachine',
initial:'red',
context:{
redCount: 0,
greenCount: 0,
yellowCount: 0,
},
states: {
red: {
//退出action
exit: assign({ redCount: (ctx) => ctx.redCount + 1 }),
on:{
click:'green',
}
},
green: {
on:{
press:{
target:'yellow',
actions: assign({ greenCount: (ctx) => ctx.greenCount + 1 }),//单个action
},
},
},
yellow: {
//进入action
entry: assign({ yellowCount: (ctx) => ctx.yellowCount + 1 }),
on:{
keyup:{
target:'red',
actions: ['countAction','doSomething'],//actions数组
},
}
},
},
},{
actions:{
countAction: assign({ count: (ctx) => ctx.greenCount + ctx.redCount + ctx.yellowCount}),
doSomething: () => console.log("为所欲为"),
}
});
//包装服务
const service = interpret(lightMachine);
//启动服务
service.start();
//获取当前上下文数据
console.log('service.state.context',service.state.value,service.state.context)
service.send('click');
console.log('service.state.context',service.state.value,service.state.context)
service.send('press');
console.log('service.state.context',service.state.value,service.state.context)
service.send('keyup');
console.log('service.state.context',service.state.value,service.state.context)
service.stop();
context是状态机声明的,与states同级,因此所有状态共享,context更接近Redux的store;
XState的行为实际就是Side Effect,可以最大限度的为XState赋能,Effect的类型较多,适用于不同的场景,具体使用可以参考官方文档,本文介绍Action的用法,Activities类似,Promise等则有自己的规则;
没有返回数据的Effect 点击跳转[7]
Actions 单次执行的,最常用
Activities 连续执行的,可将setInterval封装其内
有返回数据的Effect 点击跳转[8]
Invoked Promises
Invoked Callbacks
Invoked Observables
invoked Machines
Action通过在state中新增actions来添加
green: {
on:{
press:{
target:'yellow',
actions: assign({ greenCount: (ctx) => ctx.greenCount + 1 }),
},
},
},
green: {
on:{
press:'yellow',
}
},
有两个特殊的action,与一般的action区别在于触发的时机不同
entry:在进入状态时候触发
exit:在离开状态时候触发
action本身就是一个function,接收三個参数分別是context, event 以及 actionMeta,context 就是当前machine的context,event 则是触发当前状态切换的事件,actionMeta则存放当前的state 以及action。
上面代码出现率很高的还有assign函数,用来修改context,用法与React的setState类似;
import "./styles.css";
import * as React from "react";
import * as ReactDOM from "react-dom";
import { createMachine, assign } from "xstate";
import { useMachine } from "@xstate/react";
interface ToggleContext {
count: number;
}
const toggleMachine = createMachine<ToggleContext>({
id: "toggle",
initial: "inactive",
context: {
count: 0
},
states: {
inactive: {
on: { TOGGLE: "active" }
},
active: {
entry: assign({ count: (ctx) => ctx.count + 1 }),
on: { TOGGLE: "inactive" }
}
}
});
function App() {
const [current, send] = useMachine(toggleMachine);
const active = current.matches("active");
const { count } = current.context;
return (
<div className="App">
<h1>XState React Template</h1>
<h2>Fork this template!</h2>
<button onClick={() => send("TOGGLE")}>
Click me ({active ? "yes" : "no"})
</button>{" "}
<code>
Toggled <strong>{count}</strong> times
</code>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
import { createMachine, interpret, assign } from 'xstate';
const fetchMachine = createMachine({
id: 'Dog API',
initial: 'idle',
context: {
dog: null
},
states: {
idle: {
on: {
FETCH: 'loading'
}
},
loading: {
invoke: {
id: 'fetchDog',
src: (context, event) =>
fetch('https://dog.ceo/api/breeds/image/random').then((data) =>
data.json()
),
onDone: {
target: 'resolved',
actions: assign({
dog: (_, event) => event.data
})
},
onError: 'rejected'
},
on: {
CANCEL: 'idle'
}
},
resolved: {
type: 'final'
},
rejected: {
on: {
FETCH: 'loading'
}
}
}
});
const dogService = interpret(fetchMachine)
.onTransition((state) => console.log(state.value))
.start();
dogService.send('FETCH');
XState是一个比较纯粹的有限状态机的实现,状态驱动的思维和Redux等库还是很不同的,上文也提到过:“XState可以手动扩展来实现Redux的功能,而Redux则不能”;
XState仍处在高速发展期,国内实践较少,教程和最佳实践都比较缺乏,学习成本较大,需要依靠官网摸索;虽然XState仍在不断更新中,但是状态机的模型是确定的,因此应该不会有特别革命性的变更;
状态机的思维有些抽象,模板代码写起来也比较庞大,在具体实践中可能会遇到一些问题或者疑惑
对于明显适合状态机的场景,比如红绿灯、点赞等,有一种杀鸡用牛刀的感觉;
对于复杂的场景,需要较大的精力将状态机抽象出来,这里可能是多层嵌套的状态机,基本上需要完全理清系统的逻辑,实际做起来难度较大;
XState的一个突出的作用是可以帮助开发者理清逻辑,尤其对于复杂的系统,一个设计完备的状态机一定是可以通过状态图展示的,这是一个充分必要条件,换句话说,如果设计好的状态机不能转化为状态图,那就是设计逻辑出现了问题,这一点可以帮助开发者在开发初期就理清逻辑,将系统流程走通;
[1]JavaScript-State-Machine: https://github.com/jakesgordon/javascript-state-machine
[2]Xstate: https://github.com/jakesgordon/javascript-state-machine
[3]Redux: https://redux.js.org/
[4]Vuex: https://vuex.vuejs.org/
[5]从有限状态机、图灵机到现代计算机: https://blog.csdn.net/chenchao868/article/details/6833612
[6]xstate.js.org/viz/: https://link.juejin.cn/?target=https://xstate.js.org/viz/
[7]点击跳转: https://xstate.js.org/docs/guides/actions.html#declarative-actions
[8]点击跳转: https://xstate.js.org/docs/guides/communication.html#the-invoke-property
[9]https://xstate.js.org/viz/: https://xstate.js.org/viz/
[10]浅谈状态模式和状态机 - 掘金: https://juejin.cn/post/7005539587510517796
[11]状态模式和策略模式的区别与联系? - 知乎: https://www.zhihu.com/question/23693088
[12]策略模式 VS 状态模式 | 菜鸟教程: https://www.runoob.com/w3cnote/state-vs-strategy.html
[13]下推自动机: https://zh.wikipedia.org/wiki/%E4%B8%8B%E6%8E%A8%E8%87%AA%E5%8A%A8%E6%9C%BA
[14]什么是图灵机: https://zhuanlan.zhihu.com/p/33288542
[15]前端深水区(Deepsea)React 状态管理库研究: https://github.com/w10036w/blog/blob/master/posts/subjects/fe-state-mgmt.zh.md
[16]从有限状态机、图灵机到现代计算机_AdamChen游戏开发-CSDN博客: https://blog.csdn.net/chenchao868/article/details/6833612
[17]XState 状态机与状态图简介 【译】: https://zhuanlan.zhihu.com/p/408123696
[18]浅谈对比 XState、Redux 使用 - 掘金: https://juejin.cn/post/6844904160077283335
[19]XState 新手教學 - Context & Actions: https://blog.jerry-hong.com/posts/xstate-tutorials-context-actions/
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/rHgUYOTyMKYupuJal_vY_Q
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。