react中的狀態機
by Shawn McKay
肖恩·麥凱(Shawn McKay)
在基于狀態圖的狀態機上使用React的模式 (Patterns for using React with Statechart-based state machines)
Statecharts and state machines offer a promising path for designing and managing complex state in apps. For more on why statecharts rock, see the first article of this series.
狀態圖和狀態機為設計和管理應用程序中的復雜狀態提供了一條有希望的途徑。 有關狀態圖為何搖擺的更多信息,請參閱本系列的第一篇文章 。
But if statecharts are such an excellent solution for managing UI & state in Javascript (JS), why isn’t there more momentum behind them?
但是,如果狀態圖是用Javascript(JS)管理UI和狀態的出色解決方案,為什么它們背后沒有更多的動力呢?
One of the main reasons statecharts have not grown in popularity within the front-end world is that best practices have yet to be established. It’s not abundantly clear how to use state machines with popular component-based UI libraries such as React, Vue, or Angular.
狀態圖在前端世界中尚未普及的主要原因之一是尚未建立最佳實踐。 尚不清楚如何將狀態機與流行的基于組件的UI庫(如React,Vue或Angular)一起使用。
While it may be too early to declare best practices for statecharts in JS, we can explore some patterns used by existing state machine integration libraries.
雖然在JS中聲明狀態圖的最佳實踐為時過早,但我們可以探索現有狀態機集成庫所使用的一些模式。
狀態圖機 (Statechart machine)
Statecharts work both for visual design and as the underlying code for a graph-based state machine.
狀態圖既可以用于視覺設計,又可以用作基于圖的狀態機的基礎代碼。
Bear in mind that we’re in the early days of using statecharts with JS, and it may be worth experimenting with a variety of libraries or even developing your own. That being said, XState is currently leading the pack for statechart machine libraries in JS.
請記住,我們處于將狀態圖與JS一起使用的初期,可能值得嘗試各種庫甚至開發自己的庫。 話雖這么說, XState目前在JS中領先于Statechart機器庫。
The above state machine code can generate a much more readable statechart diagram when passed as JSON to the XState Visualizer.
上面的狀態機代碼作為JSON傳遞給XState Visualizer時,可以生成更具可讀性的狀態圖。
You can even work the other way, starting by visually designing and then exporting to an XState configuration using sketch.systems. We don’t have all the pieces in one place yet, but there are no serious technical barriers to an open source solution.
您甚至可以以其他方式工作,從視覺設計開始,然后使用sketch.systems導出到XState配置。 我們還沒有將所有內容都放在一個地方,但是開源解決方案沒有嚴重的技術障礙。
Now that we have an idea of what XState does, let’s look at what it doesn’t do.
既然我們已經了解了XState的功能,那么讓我們看看它沒有做什么。
XState Tagline: “stateless finite state machines and statecharts”.
XState口號:“無狀態的有限狀態機和狀態圖”。
So what does it mean for a state machine to be stateless?
那么,狀態機是無狀態的意味著什么呢?
無狀態機器 (Stateless machines)
Stateless machines offer an unopinionated blueprint for state management — a kind of “roll your own” solution that doesn’t dictate where or how state in your application is stored.
無狀態機器為狀態管理提供了一個不受質疑的藍圖 -一種“滾動自己的”解決方案,它不指示應用程序中狀態的存儲位置或方式。
Much like a presentational component, a stateless machine is made of pure functions, is immutable, and maintains no state. It tracks no past, current, or future — but it can be used to help you calculate each.
無狀態機器很像表示組件,是由純功能組成的,是不變的,并且不保持任何狀態。 它不會跟蹤過去,現在或將來,但是可以用來幫助您計算每個。
Managing your state can be as easy as storing it in a local state variable.
管理狀態就像將其存儲在本地狀態變量中一樣容易。
Stateless machines don’t give you much out of the box. To trigger a transition, we must always pass in the current state node in to find the next. XState can let you know which actions should be fired on each state change, but you’ll have to find a way to manage the actions yourself.
無狀態機器無法為您提供很多便利。 要觸發轉換,我們必須始終傳入當前狀態節點以查找下一個。 XState可以讓您知道在每次狀態更改時應觸發哪些操作,但是您必須找到一種自行管理操作的方法。
If you’re interested in a more complete solution, consider making your state machine stateful.
如果您對更完整的解決方案感興趣,請考慮使狀態機變為有狀態。
狀態機 (Stateful machines)
A stateful machine tracks your node position on the state graph and manages the firing of actions. There is no need to pass in the current state on transitions — it tracks your current state node.
有狀態機會跟蹤您在狀態圖上的節點位置并管理動作的觸發。 過渡時無需傳遞當前狀態-它會跟蹤您的當前狀態節點。
As a summary, the instance of the stateful machine above:
作為總結,上述有狀態機的實例:
- determines the green state position at “Ringing” 確定“ Ring”處的綠色狀態位置
limits the possible purple’active transition events to
CANCEL
orSNOOZE
將可能的“紫色”活動過渡事件限制為“
CANCEL
或“SNOOZE
fires the
startRing
action on entry在進入時觸發
startRing
操作fires the
stopRing
action on leaving the state離開狀態時觸發
stopRing
動作
Of course, there is more than one way to create a stateful machine. We’re back to the question of where to manage state:
當然,創建狀態機的方法不止一種。 我們回到了在哪里管理狀態的問題:
- within the existing component state? 在現有組件狀態內?
- in a connected state machine? 在連接狀態機中?
Let’s explore some design patterns with examples, starting with stateful components.
讓我們從狀態組件開始,通過示例探索一些設計模式。
有狀態的組件 (Stateful components)
A stateful component, as you might imagine, manages state within the component, or within a wrapping higher-order component. In React, this would be as state
. Storing state within a UI library ensures that changes won’t be missed and will trigger re-renders.
您可能會想到,有狀態組件將管理該組件內或包裝的高階組件內的狀態。 在React中,這將是state
。 將狀態存儲在UI庫中可確保不會丟失更改,并會觸發重新渲染。
This is the approach of a library called React-Automata that uses a higher-order component initiated by withStatechart
.
這是稱為React-Automata的庫的方法,該庫使用由withStatechart
發起的高階組件。
React-Automata offers several patterns for using statecharts with components:
React-Automata提供了幾種使用狀態圖和組件的模式:
- state from props 道具狀態
- conditional rendering from a context 上下文中的條件渲染
- state from actions 從行動狀態
We’ll go over each pattern and consider the pros and cons.
我們將仔細研究每種模式,并考慮其優缺點。
道具狀態 (State from Props)
Passing state directly into components seems like the most obvious solution.
將狀態直接傳遞到組件似乎是最明顯的解決方案。
In React-Automata, state can be passed by accessing it on the machineState
prop — a reference to the actual state machine.
在React-Automata中,可以通過在machineState
道具上訪問狀態來傳遞狀態,該狀態是對實際狀態機的引用。
But be wary, this is by no means best practice. In the example above, the integration has coupled the statechart to the component, leading to a poor separation of concerns.
但是要小心, 這絕不是最佳實踐 。 在上面的示例中,集成將狀態圖耦合到了組件,從而導致關注點分離不佳。
Consider that the statechart and components can allow for a clean divide as they solve different problems:
考慮到狀態圖和組件可以解決不同的問題,因此可以進行清晰的劃分:
statecharts: when things happen, for example, enter state, actions fired
狀態圖: 當事情發生時,例如進入狀態,觸發動作
components: how and what happens, for example, the view, user interactions
組件: 如何以及如何發生,例如視圖,用戶交互
Alternatively, you could decouple the component from the state machine by conditionally rendering with a default of no render.
或者,您可以通過默認不渲染的有條件渲染來將組件與狀態機分離。
Certainly, there must be a more natural way to set up conditional rendering without having to turn all your renders into if/else
and switch
statements.
當然,必須有一種更自然的方法來設置條件渲染,而不必將所有渲染都轉換為if/else
和switch
語句。
從上下文進行條件渲染 (Conditional rendering from a context)
State accessed by a context doesn’t need to be passed directly.
上下文訪問的狀態不需要直接傳遞。
React-Automata provides a pattern for conditional rendering of child components using React’s context and a <Sta
te> component. Note that the
value property can match on a string, array of strings, or even a glob-based pattern.
React-Automata提供了一種使用React上下文和<Sta
te>組件有條件地渲染子組件的模式。 注意THA t the
值屬性可以匹配的字符串,字符串數組,或甚至基于水珠圖案。
If the state value matches Ringing
, the children inside of the State
component will render. Otherwise, nothing.
如果狀態值匹配Ringing
,則將渲染State
組件內的子級。 否則,什么都沒有。
State from context can help clarify the number of possible finite state view combinations. As in the case above, it’s clear there are only two possible configurations.
上下文中的狀態可以幫助闡明可能的有限狀態視圖組合的數量。 與上面的情況一樣,很明顯只有兩種可能的配置。
If view configurations start to get out of hand, React-Automata offers a render prop pattern that passes in a boolean based on the value.
如果視圖配置開始失控,React-Automata將提供一個渲染道具模式,該模式根據該值傳入布爾值。
Similarly, it’s possible to conditional render based on context actions.
同樣,有可能根據上下文操作進行條件渲染。
Conditionally rendering based on state or actions maintains a coupling between the statechart and components, but less explicitly through context. How might you give components their isolated state apart from statecharts?
基于狀態或動作的有條件渲染可維持狀態圖和組件之間的耦合,但通過上下文不太明確。 除了狀態圖,您如何賦予組件孤立的狀態?
從行動狀態 (State from actions)
It’s possible to use statecharts to update the internal state of a linked component using actions as triggers.
可以使用狀態圖以動作作為觸發器來更新鏈接組件的內部狀態。
React-automata checks the methods on a component and calls the functions if the names match the actions being fired.
React-automata檢查組件上的方法,并在名稱與要觸發的動作匹配時調用函數。
As an example, the onEntry action startRing
is fired as the state machine enters Ringing
, causing the AlarmClock
state to change to ringing
. On leaving the Ringing
state, stopRing
is fired, and ringing
is set to false
.
例如,當狀態機進入Ringing
,會觸發onEntry操作startRing
,導致AlarmClock
狀態更改為ringing
。 退出Ringing
狀態時,將觸發stopRing
,并將ringing
設置為false
。
Note that, although of these methods are called with params, the methods already have access to whatever they need from machineState
through props.
請注意,盡管這些方法是用params調用的,但這些方法已經可以通過props從machineState
訪問所需的machineState
。
Using internal component state managed through actions leads to a strong decoupling of components from state charts. However, it can also create a degree of clutter or confusion in components. It is not explicitly clear how or when methods will be called without examining the names of actions in the statechart. For this reason, I often call my actions and methods enterX
or exitX
in order to make it explicitly clear why and where they are being fired.
使用通過操作管理的內部組件狀態會導致組件與狀態圖之間的強烈分離。 但是,它也會在組件中造成一定程度的混亂或混亂。 在不檢查狀態圖中動作名稱的情況下,尚不清楚如何或何時調用方法。 因此,我經常將我的操作和方法enterX
或exitX
,以便清楚地闡明其原因和觸發位置。
外部狀態機 (External state machines)
Another option worth considering is storing state outside of your UI framework. As with other state management libraries like Redux, components can be connected to an external state machine and updated with “on state change” and “on action” events.
另一個值得考慮的選擇是將狀態存儲在UI框架之外。 與Redux等其他狀態管理庫一樣,組件可以連接到外部狀態機,并通過“狀態更改”和“動作中”事件進行更新。
As an example, XStateful is a wrapper around XState that handles state, transitions, emitting events, triggering actions, and more.
例如, XStateful是XState的包裝,用于處理狀態,轉換,發出事件,觸發動作等。
XStateful works well with a React connector called XStateful-React.
XStateful與一個名為XStateful-React的React連接器很好地配合使用。
XStateful-React has much in common with React-Automata. But there is at least one signficant difference — the state machine instance is not managed within any component.
XStateful-React與React-Automata有很多共同點。 但是,至少有一個顯著的區別-狀態機實例不在任何組件中進行管理。
So how does external state from reducers work in XStateful?
那么,Reducer的外部狀態如何在XStateful中工作?
狀態和數據 (State and data)
Applications often require more than just the state node in a state graph— they require data as well. Often this data needs to be synced across components, in a way that can be frustrated if it must be passed from the uppermost shared parent.
應用程序通常不僅需要狀態圖中的狀態節點,還需要數據。 通常,此數據需要跨組件同步,如果必須從最上層共享父級傳遞數據,可能會感到沮喪。
There are existing popular solutions for syncing data, such as Redux, or my state management wrapper for Redux. Unfortunately, these don’t play well with many state wrappers such as React-Automata due to an open issue with passing refs in React Redux (see this open issue with connect() and React.forwardRef).
有用于同步數據的現有流行解決方案,例如Redux或我的 Redux 狀態管理包裝器 。 不幸的是,由于在React Redux中傳遞引用存在開放性問題 (請參見connect()和React.forwardRef的此開放性問題) ,這些方法在許多狀態包裝器(例如React-Automata)中無法很好地發揮作用。
A complete state solution should manage both state and data.
完整的狀態解決方案應同時管理狀態和數據。
XStateful offers just such a state and data solution using a state reducer pattern, similar to Redux.
XStateful使用狀態減少器模式提供了這種狀態和數據解決方案,類似于Redux。
State machine subscribers listen and update changes based on actions emitted from the state machine. Note that XState refers to data as extended state, or extstate
.
狀態機訂戶根據狀態機發出的操作來偵聽和更新更改。 請注意,XState將數據稱為擴展狀態或extstate
。
This particular Reducer pattern may seem unfamiliar, however, it’s heavily used in projects such as ReasonReact.
這種特殊的Reducer模式可能看起來并不熟悉,但是它在諸如ReasonReact之類的項目中大量使用。
Data can also be accessed in conditional renders on the property cond
.
也可以在屬性cond
條件渲染中訪問數據。
Be careful with using state to conditionally render components, as it creates a non-deterministic set of possible states. No longer are you limited to the number of states, but now to the number of state and data combinations. You lose out on deterministic features, discussed later in the testing section.
使用狀態來有條件地渲染組件時要小心 ,因為它會創建一組不確定的可能狀態。 您不再局限于狀態數,而是現在僅限于狀態數和數據組合數。 您將失去確定性功能,稍后在測試部分中進行討論。
This data can be passed into your component using a render prop pattern.
可以使用渲染道具圖案將此數據傳遞到您的組件中。
There is less of a need for state management tools like Redux if data can be stored within a complete state machine tool like XStateful.
如果可以將數據存儲在完整的狀態機工具(例如XStateful)中,則對Redux之類的狀態管理工具的需求就更少了。
測試中 (Testing)
State machines also offer a better path for front-end testing.
狀態機還為前端測試提供了更好的途徑。
The deterministic nature of state machines creates the possibility of simplified front-end testing.
狀態機的確定性性質提供了簡化前端測試的可能性。
In React-Automata you can autogenerate snapshot tests using testStatechart
, a method that takes the XState configuration and the component.
在React-Automata中,您可以使用testStatechart
自動生成快照測試,該方法采用XState配置和組件。
testStatechart
runs through the state graph and creates a Jest snapshot test for each possible configuration of the component. It will toggle on and off your various <State
/>, <
Action /> components, leading to a recording of all possible conditional rendering combinations.
testStatechart
貫穿狀態圖,并為組件的每個可能配置創建一個Jest快照測試 。 它將打開和關閉各種<State
/ >, <
Action />組件,從而記錄所有可能的條件渲染組合。
開發工具 (Devtools)
Devtools play an active role in what makes a library developer-friendly — debugging can be the hardest or most straightforward part of your job.
Devtools在使庫對開發人員友好的過程中扮演著積極的角色-調試可能是您工作中最困難或最直接的部分。
In this respect, React-Automata offers a helpful integration via Redux Devtools. Each connected component becomes a named instance in the devtools, and each transition and action are displayed chronologically as actions are presented in Redux devtools.
在這方面,React-Automata通過Redux Devtools提供了有用的集成。 每個連接的組件在devtools中成為一個命名實例,并且每個動作和動作都按時間順序顯示在Redux devtools中。
XState offers an entirely new set of variables to track. Consider the following example by Erik Mogensen on the kinds of information an XState debugger may track.
XState提供了一組全新的變量來跟蹤。 考慮一下Erik Mogensen的以下示例,該示例涉及XState調試器可能跟蹤的信息。
This is not to say that state machine devtools need to look like our existing devtools. State machine devtools present an opportunity for a more visual debugging experience.
這并不是說狀態機devtools必須看起來像我們現有的devtools。 狀態機devtools提供了獲得更直觀的調試體驗的機會。
結論 (Conclusion)
While we’re still in the early days of statecharts in JS, there are enough options available to start developing applications on top of XState. We can learn from these development patterns to both improve available libraries and to create tools to support the enormous potential of visual-based programming.
雖然我們仍處于JS狀態圖的早期,但是有足夠的可用選項來開始在XState之上開發應用程序。 我們可以從這些開發模式中學習,以改進可用的庫并創建工具來支持基于視覺的編程的巨大潛力。
Having developed applications with statecharts over the past three months, I’ve personally found these new patterns to be a breath of fresh air. Collaboration has become much more comfortable, as team members can visually grasp the underlying logic of a significant and growing system.
在過去三個月中使用狀態圖開發了應用程序之后,我個人發現這些新模式令人耳目一新。 由于團隊成員可以從視覺上掌握重要且不斷發展的系統的基本邏輯,因此協作變得更加舒適。
My hope is that this article will help others find statechart-based development more approachable. If you found it helpful, give a clap and pass it on :)
我希望本文將幫助其他人發現基于狀態圖的開發更加平易近人。 如果您覺得有用,請鼓掌并繼續進行:)
翻譯自: https://www.freecodecamp.org/news/patterns-for-using-react-with-statechart-based-state-machines-33e6ab754605/
react中的狀態機