前端后端【免费下载链接】xstateState machines, statecharts, and actors for complex logic项目地址https://gitcode.com/gh_mirrors/xs/xstate点击查看免费下载导读本文围绕当前仓库中的 examples/todomvc-react/readme.md 展开系统讲解如何用XState v5 React TypeScript Vite构建一个完整的 TodoMVC 应用。文中不仅复现了原文档给出的本地运行方式还深入剖析了仓库中 todosMachine.ts 与 Todo.tsx 的源码实现带你掌握「全局列表状态机 单条待办编辑状态机」的双层建模思路以及createActorContext、useSelector、setup().provide()等 XState v5 核心 API 的实战用法。读完本文你将能够独立搭建一个具备增删改查、过滤、全选/清除已完成、URL Hash 路由与 localStorage 持久化的 TodoMVC 应用。示例概览与技术栈TodoMVC 是前端社区公认的经典参考应用覆盖了一个列表型业务场景的绝大多数交互新增待办、编辑待办、标记完成、删除、批量标记、按状态过滤、清除已完成。本仓库用 XState v5 完整实现了这些功能而不是把状态散落在 React 组件的useState中。该示例的技术栈如下依据 package.json技术版本范围作用xstate^5.28.0状态机核心定义 todos 与单条 todo 的状态逻辑xstate/react^4.1.3React 集成提供createActorContext、useSelector、useActorRefreact/react-dom^17.0.2UI 渲染typescript^5.9.3类型安全状态机事件与 context 全程类型化vite1.0.0-rc.13开发服务器与构建classnames^2.5.1条件类名拼接选中过滤项、编辑态高亮等todomvc-app-css/todomvc-common^2.4.3/^1.0.5TodoMVC 官方样式入口 main.tsx 引入todomvc-common/base.css与todomvc-app-css/index.css因此无需手写 UI 样式可以把全部精力聚焦在状态建模上。本地运行指南按照原文档给出的步骤在仓库根目录下依次执行cd examples/todomvc-react pnpm install pnpm run devpnpm install基于仓库已有的 pnpm-lock.yaml 安装依赖本仓库使用 pnpm workspace 管理多包示例目录内锁文件已固定版本可保证依赖可复现pnpm run dev启动 Vite 开发服务器默认监听localhost:5173浏览器打开即可交互若需生产构建可运行 package.json 中定义的pnpm run build等价于tsc vite build先做 TypeScript 类型检查再打包。原文档还提供了 CodeSandbox 与 StackBlitz 两种在线沙箱入口适合不搭建本地环境时快速体验。无论本地还是沙箱代码与依赖完全一致。双层状态机架构列表机与条目机TodoMVC 的交互天然可以拆成两层全局列表层所有待办的新增、删除、标记、过滤与单条待办层双击进入编辑、编辑中保存/取消。示例用两台状态机分别建模这是理解整个示例的钥匙。顶层todosMachine 管理列表与过滤todosMachine.ts 使用 XState v5 的setup()createMachine()声明式建模export const todosMachine setup({ types: {} as { context: { todo: string; todos: TodoItem[]; filter: TodosFilter; }; events: | { type: newTodo.change; value: string } | { type: newTodo.commit; value: string } | { type: todo.commit; todo: TodoItem } | { type: todo.delete; id: string } | { type: filter.change; filter: TodosFilter } | { type: todo.mark; id: string; mark: active | completed } | { type: todo.markAll; mark: active | completed } | { type: todos.clearCompleted }; } }).createMachine({ id: todos, context: { todo: , todos: [{ id: 1, title: Learn state machines, completed: false }], filter: all }, on: { /* 8 个事件处理 */ } });值得注意的设计要点context 的三块数据todo保存输入框的实时文本todos是TodoItem[]列表{ id, title, completed }filter是all | active | completed三态过滤条件types: {} as {...}泛型标注让所有事件和 context 获得完整的 TypeScript 推断组件里send事件时字段错误会直接编译报错顶层on而非具名状态列表本身没有复杂的「阶段」切换用机器级全局事件处理即可每个事件映射一个assign更新 action守卫guard内联在事件配置中newTodo.commit通过guard: ({ event }) event.value.trim().length 0拒绝空白输入只有通过守卫的事件才会执行新增 action——这是 XState v5 用条件转移替代手写if的典型写法。各事件的行为可对照源码逐条理解事件守卫/行为对应 context 变更newTodo.change无todo更新为输入值newTodo.commit非空trim 后清空todo追加{ id, title, completed: false }id用Math.random().toString(36).substring(7)生成todo.commit无标题为空则删除该条否则用编辑后的todoToUpdate替换原条目todo.delete无按id过滤移除filter.change无更新filtertodo.mark无按id将对应条目completed置为mark completedtodo.markAll无全部条目的completed统一置为marktodos.clearCompleted无过滤掉所有completed true的条目一个值得学习的细节todo.commit的更新是不可变更新——用context.todos.map(...)生成新数组、对目标条目返回todoToUpdate其余返回原对象。这保证了状态机快照的不可变性也是useSelector能精确触发重渲染的前提。底层todoMachine 建模单条编辑状态双击一条待办会进入编辑态编辑中涉及「保存 / 取消 / 内容变化」这天然是一个小型状态机。Todo.tsx 中定义了export const todoMachine setup({ types: { context: {} as { initialTitle: string; title: string }, events: {} as | { type: edit } | { type: blur } | { type: cancel } | { type: change; value: string }, input: {} as { todo: TodoItem } }, actions: { focusInput: () {}, onCommit: () {} } }).createMachine({ id: todo, initial: reading, context: ({ input }) ({ initialTitle: input.todo.title, title: input.todo.title }), states: { reading: { on: { edit: editing } }, editing: { entry: [ assign({ initialTitle: ({ context }) context.title }), { type: focusInput } ], on: { blur: { target: reading, actions: onCommit }, cancel: { target: reading, actions: assign({ title: ({ context }) context.initialTitle }) }, change: { actions: assign({ title: ({ event }) event.value }) } } } } });这段源码展示了 XState v5 的多个关键能力显式状态机而非布尔开关reading/editing两个命名状态避免了用isEditing布尔变量在多个组件间手动同步input类型与context初始化器types.input声明机器创建时的外部输入context: ({ input }) ...将传入的TodoItem映射为内部 context。这是 v5 取代旧版context静态对象的正式做法entry动作数组进入editing时先assign记录initialTitle作为取消编辑的还原基准再触发focusInput自定义 actioncancel组合转移目标reading的同时用assign把title还原为initialTitle一个事件配置内同时完成状态切换与数据回滚声明式动作 运行时注入setup({ actions: { focusInput, onCommit } })只声明动作签名具体实现由组件通过.provide()注入见下文「React 集成」部分。React 集成Actor Context 与选择器有了状态机接下来是把它们接入 React。示例的关键做法是「一个全局 Actor 上下文 每个组件按需订阅切片」。createActorContext 与初始快照注入App.tsx 创建全局上下文并把localStorage中的持久化快照作为初始状态注入export const TodosContext createActorContext(todosMachine, { state: JSON.parse(localStorage.getItem(todos) || null) }); function App() { return ( TodosContext.Provider Todos / /TodosContext.Provider ); }createActorContext(machine, options)返回带Provider、useActorRef、useSelector等方法的上下文对象第二个参数{ state: ... }是状态恢复rehydration页面刷新时把上次保存的快照反序列化后直接作为初始状态而不是回到初始 context。若localStorage中无数据null则使用机器默认的初始 context。useSelector 精确订阅Todos.tsx 中不再用useMachine拿整份状态而是按需选取切片const { send } TodosContext.useActorRef(); const todo TodosContext.useSelector((s) s.context.todo); const todos TodosContext.useSelector((s) s.context.todos); const filter TodosContext.useSelector((s) s.context.filter);useSelector返回的快照值由选择函数selector推导内部做浅比较只有选中片段变化时才触发组件重渲染。例如输入框只订阅s.context.todo列表只订阅s.context.todos过滤链接只订阅s.context.filter——三块数据互不干扰从源码结构看这是为了把重渲染粒度压到最小。过滤逻辑filterTodos是纯函数active返回未完成项、completed返回已完成项、all原样返回在渲染前对todos做投影状态机本身不保存「过滤后的列表」保证单一数据源。useActorRef 与子 ActorTodos.tsx 通过TodosContext.useActorRef()拿到列表 Actor 的引用从而调用subscribe做副作用持久化。而 Todo.tsx 中每条待办使用useActorRef创建独立的todoMachine实例并用provide注入动作实现const todoActorRef useActorRef( todoMachine.provide({ actions: { onCommit: ({ context }) { todosActorRef.send({ type: todo.commit, todo: { ...todo, title: context.title } }); }, focusInput: () { setTimeout(() inputRef.current inputRef.current.select()); } } }), { input: { todo } } );这里的信息量很大todoMachine.provide({ actions: {...} })把声明式动作替换为真实实现onCommit通过闭包捕获的todosActorRef向列表机发送todo.commit——子机与父机通过事件通信而不是直接改对方的 context保持了 Actor 模型的消息传递边界useActorRef的第二个参数{ input: { todo } }对应机器的types.input把当前条目注入 context 初始化器条目机的发送通过const { send } todoActorRef进行双击触发edit回车/失焦触发blurEscape 触发cancel输入过程触发change使用useSelector(todoActorRef, ...)分别订阅s.context.title与s.matches(editing)前者驱动输入框值后者驱动li的editing类名实现双击进入编辑态的高亮。持久化把快照写回 localStorageTodoMVC 的经典要求是刷新后数据不丢失。本示例的持久化策略是「快照订阅」在 Todos.tsx 中订阅 Actor 的每次状态变化并序列化useEffect(() { todosActorRef.subscribe(() { localStorage.setItem( todos, JSON.stringify(todosActorRef.getPersistedSnapshot?.()) ); }); }, [todosActorRef]);todosActorRef.subscribe(cb)在每次状态变更时回调回调内把快照写入localStorage的todos键getPersistedSnapshot()返回适合序列化的快照形态v5 中持久化相关 API用于跨会话恢复与 App.tsx 中createActorContext的state选项形成「存」与「取」的闭环可选链?.保留了对旧版本 API 的兼容性。需要留意由于初始化时也会触发订阅回调配合 App.tsx 的state恢复用户刷新页面后列表、过滤状态与输入框文本都能完整还原。URL Hash 驱动过滤条件示例没有引入路由库而是用浏览器原生hashchange事件把过滤条件映射到 URL Hash#/、#/active、#/completeduseHashChange.ts 封装了hashchange监听与清理的useEffectTodos.tsx 中监听变化并发送filter.changefilter: (window.location.hash.slice(2) || all)即去掉#/前缀后作为过滤值初次挂载时读取当前 hash 同步一次过滤状态useEffect空依赖数组确保刷新或直接带 hash 进入页面时过滤条件正确底部三个过滤链接分别href#/、href#/active、href#/completed点击即改变 hash 并触发事件。这种「hash 即状态」的做法让过滤条件可被收藏、可分享且无需引入额外路由依赖。关键源码导读如果你想深入阅读实现细节建议按以下顺序浏览路径均相对于仓库根目录todosMachine.ts顶层列表状态机8 个事件的完整定义与 context 初始值Todo.tsx单条待办编辑状态机todoMachine、provide动作注入与子 Actor 通信App.tsxcreateActorContext创建全局上下文并注入持久化快照Todos.tsxuseSelector切片订阅、subscribe持久化、hash 过滤与 UI 装配main.tsx入口与 TodoMVC 样式引入package.json依赖版本与dev/build脚本。小结从本示例可以提炼出一套可复用的 XState v5 应用架构用setup()声明类型化的事件与 context → 用createMachine()建模列表与条目两层状态机 → 用createActorContext提供全局 Actor → 用useSelector按切片订阅 → 用subscribegetPersistedSnapshot完成持久化 → 用hashchange把 UI 状态同步到 URL。这套组合兼顾了类型安全、可测试性与组件渲染性能是状态管理从「组件内布尔变量」升级为「可描述、可序列化的显式状态机」的完整范例。赞分享前端后端【免费下载链接】xstateState machines, statecharts, and actors for complex logic项目地址https://gitcode.com/gh_mirrors/xs/xstate点击查看免费下载相关推荐CANN pyasc 数据搬运接口 asc.data_copy_pad 详解非对齐搬运与填充实战CANN pyasc 数据搬运接口 asc.data_copy_pad 详解非对齐搬运与填充实战 导读 asc.data_copy_pad 是 CANN py前端后端用 Yew 实现 TodoMVC状态持久化、NodeRef 与完整组件状态管理实战解析用 Yew 实现 TodoMVC状态持久化、NodeRef 与完整组件状态管理实战解析 导读 本文以 Yew 官方仓库中的 todomvc 示例 https:前端Web框架SukiUI实战项目从零搭建现代化桌面应用界面SukiUI实战项目从零搭建现代化桌面应用界面 SukiUI是一个专为AvaloniaUI打造的现代UI主题框架它提供了丰富的控件库和灵活的主题系统帮助开UI组件桌面应用上一篇Slang IR 设计深度解析从万物皆指令到全局值去重下一篇使用 Terraform 将 streetmerchant 部署到 AWS ECS Fargate基础设施即代码实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考