Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/brucou/react-state-driven
Adding extended hierarchical state machine capabilities to React (1K min.gzipped)
https://github.com/brucou/react-state-driven
functional-reactive-programming react reactjs state-machine statechart
Last synced: about 2 months ago
JSON representation
Adding extended hierarchical state machine capabilities to React (1K min.gzipped)
- Host: GitHub
- URL: https://github.com/brucou/react-state-driven
- Owner: brucou
- License: mit
- Created: 2018-10-23T19:08:22.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2020-06-16T10:36:02.000Z (over 4 years ago)
- Last Synced: 2024-11-11T07:38:52.350Z (2 months ago)
- Topics: functional-reactive-programming, react, reactjs, state-machine, statechart
- Language: JavaScript
- Homepage:
- Size: 1.43 MB
- Stars: 22
- Watchers: 3
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
- [Motivation](#motivation)
- [Installation](#installation)
- [API](#api)
* [` `](#---machine-fsm--renderwith--commandhandlers--eventhandler---preprocessor---effecthandlers---options-----)
* [Example with mandatory props](#example-with-mandatory-props)
* [Example with optional props](#example-with-optional-props)
+ [Using a preprocessor](#using-a-preprocessor)
+ [Using effect handlers](#using-effect-handlers)
+ [Customizing rendering](#customizing-rendering)
+ [Customizing options](#customizing-options)
+ [Command handlers](#command-handlers)
+ [A typical machine run](#a-typical-machine-run)
* [Types](#types)
* [Contracts](#contracts)
- [Semantics](#semantics)
- [Architecture](#architecture)
- [Code examples](#code-examples)
- [API design goals](#api-design-goals)
- [Tips and gotchas](#tips-and-gotchas)
- [Prior art and useful references](#prior-art-and-useful-references)# Motivation
User interfaces are reactive systems which can be modelized accurately by state machines. There is a number of state machine libraries in the field with varying design objectives. We have proposed
a state machine library with a minimal API, consisting of a single effect-less function.
In this particular design, the machine is a function which takes inputs (events to be processed
by the machine) and outputs commands to be executed. While it is entirely possible to modelize
an entire web application with state machines, a common use case is to modelize a
component, and reuse that component.
This library proposes an integration of the Kingly state machine library with React that takes
the shape of a `` component which can be used in a React application like any other
components. The `` component will listen to user events, compute commands and execute
them, generally leading to updating the screen.# Installation
`react` is a peer dependency. The component has been tested with React 16.4 and above. It may
however work with lower versions of React as it does not use hooks, context, etc. If you
encounter an issue with a given version of React, please log an issue.```sh
npm install react-state-driven
```# API
## ` `
We expose a `` React component which will hold the state machine and implement its
behaviour using React's API. The `Machine` component behaviour is specified by its props.There are 3 mandatory props and four optional props. The three mandatory props are the machine
specifying the component's behavior, the component to render the screens with, and the command
handlers to execute the commands computed by the machine.The four optional props exists to address more complex use cases without modifying the
`` component. This is in line with the open-closed principle which states that
"software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification".We will examine these use cases in a dedicated section. Let's now describe the semantics of the
component:- When the React `Machine` component is mounted, the `fsm` machine will be sent an event
(called `MOUNTED` by default, and configurable in `options.initialEvent`). From that event, the
machine generally computes the initial screen to be rendered. In fact the machine computes a
`RENDER` command, which include the props for the `renderWith` component whose single
responsibility is to render the screens for the `Machine` component.
- Assuming `fsm({[MOUNTED]: void 0}) = [{command: RENDER, params: initialProps}]`, and
`renderWith = Component` the screen `` will be displayed when
the `Machine` component is mounted
- The `` rendering screens is injected by `` with a `next` prop, which is
an event dispatcher whose end is connected to the `fsm` machine. That means that `next(event)`
will trigger the computation of `fsm(event)`. The `next` prop is thus used by the `Component` to
pass events that to the machine. Typically, as `Component` only handles rendering, the
aforementioned events are DOM or user events.
- The `fsm` machine computes commands in response to received events. The `commandHandlers`
prop assigns handlers for every possible command generated by the machine. Computed commands are
executed with the dedicated handler. Handlers are passed the `next` dispatcher as it is often
necessary to pass back to the machine the results of an executed command. For instance, if a
command fetches remote data, that remote data may be fed back to the machine in the shape of a
`SUCCESS` event. Conversely, an `ERROR` event may be sent back to indicate that the fetching has
failed.The previous paragraph describes how the mandatory props (`fsm`, `renderWith`, `commandHandlers`) are used. Let's talk about the optional props:
- `eventHandler` defaults to a minimal event emitter based on [`emitonoff`](https://github.com/konsumer/emitonoff), a 300B library. You can provide your own event emitter fitting your own purposes. The event handler is an object with a `next` method. If no preprocessor is used, then additionally the event handler should have a `subscribe` method which when called returns an object with an `unsubscribe` method. This means that using a Subject as event handler is as easy as `eventHandler = new Rx.Subject()`. The event handler may also have `error` and `complete` method if the API user wants to deal with errors and disposal of resources in some ad-hoc ways.
- the `preprocessor` is receiving all events sent by the `next` dispatcher and converting them into events for the `fsm` machine. The preprocessor defaults to `x => x` i.e. the identity function.
- `effectHandlers` allows command handlers to be more single-concern. Executing a command may involve realizing and coordinating several effects. In a clean architecture, the command handlers should only orchestrate the sequence of effects realizing the commands, and the effects should only, well, execute a single effect. This is useful for testing purposes, as orchestration can be tested separately from effect execution.
- `options` is designed to be a bag that gathers all customization opportunities which do not belong to any of the other classifications. As of now, it includes an `initialEvent` property which configured the event that will be run through the machine when mounting the `` component|Option|Use case|
|---|---|
| eventHandler |- pass events from outside the component or from outside the DOM, for instance to communicate with other components|
|preprocessor|- convert one DOM event into one semantic event. For instance, convert a button click (no semantics) into a search intent or submit intent (note the semantics).|
| |- aggregate several DOM events into one event. For instance, throttle button clicks|
| |- pass events from outside the component or from outside the DOM, for instance to communicate with other components|
|effectHandlers|- large component, or complex behavior where clean code matters|
| |- customizing render handler (default is React.setState), for instance running some operations before or after rendering|
|options|- configure initial event. This also entirely decouples the `fsm` machine from the `Machine` component|Note that our `Machine` component expects some props but does not expect children components.
We provide two examples, one illustrating the mandatory props, the second illustrating all optional props.
## Example with mandatory props
This example only uses the mandatory props. It also uses JSX, and JSON patch to update the machine's extended state. We use JSON patch mainly to provide an example of configuration of `updateState` property. You may use a simple `Object.assign`-based reducer, or Immer or any other reducing function.For illustration, the behavior modelization is as follows:
![image search interface](https://res.infoq.com/articles/robust-user-interfaces-with-state-machines/en/resources/18-movie-search-good-fsm-corrected-flowchart%20no%20emphasis%20switchMap-1555879148127.jpg)
The demo application can be evaluated in a [codesandbox playground](https://codesandbox.io/s/react-movie-app-state-driven-with-default-options-18m62?file=/src/MovieSearch.js).
## Example with optional props
This example uses the optional props. It uses hyperscripts instead of JSX and a Rxjs Subject for event emitter, and JSON patch to update the machine's extended state.We will implement an [image search application](https://css-tricks.com/robust-react-user-interfaces-with-finite-state-machines/#article-header-id-5).
That application basically takes an input from the user, looks up images related to that search input, and displays them. The user can then click on a particular image to see it in more details.For illustration, the user interface starts like this:
![image search interface](https://i.imgur.com/mDQQTX8.png?1)
[Click here](https://codesandbox.io/s/flickr-search-app-with-kingly-qivl3) for a live demo.
The user interface behaviour can be modelized by the following machine:
![machine visualization](assets/image%20gallery%20state%20cat.png)
The visual notation for the modelization is addressed in Kingly documentation. We recall here the main points:
- the black bullet (entry point) from our machine graph corresponds to a `init` control state, which moves to the `start` control state with the initial event `START`.
- `events` and `states` respectively are a list of events and control states accepted and represented in the machine
- `initialControlState` and `initialExtendedState` encode the initial state for the machine
- the `transitions` property of the machine encodes the edges of the graph that modelizes the behaviour of the interface
- `updateState` specifies how to update the extended state of the machine from a description of the updates to perform. We use [JSON patch](http://jsonpatch.com/) in our example. A redux-like reducer, proxy-based `immer.js` or any user-provided function could also be used, as long as it respects the defined interface.### Using a preprocessor
In this example, we translate DOM events into machine events with a preprocessor. The preprocessor is a function which receives the event handler as parameter (named `rawEventSource` here). As the event handler chosen is a Rx.js Subject, we can use `pipe`, `map` and all the Rx.js combinators to perform our event conversion. This example for instance eliminates all unnecessary information from the DOM events and React framework (`ref`), so the machine events are completely decoupled from the DOM.We thus move from `onSubmit`,`onCancelClick`, `onGalleryClick`, `onPhotoClick` DOM events to `SEARCH`, `CANCEL_SEARCH`, `SELECT_PHOTO`, `EXIT_PHOTO` machine events.
The preprocessor must return an object with a `subscribe` property by which the values computed in the preprocessor will flow. In other words, the preprocessor must return an observable. The `subscribe` method must return an object with an `unsubscribe` method which will be called when the `` component is unmounted. The API is thus designed to be a perfect fit for Rxjs Subjects and Observables.
```js
...
preprocessor: rawEventSource =>
rawEventSource.pipe(
map(ev => {
const { rawEventName, rawEventData: e, ref } = destructureEvent(ev);if (rawEventName === INIT_EVENT) {
return { [INIT_EVENT]: void 0 };
}
// Form raw events
else if (rawEventName === "START") {
return { START: void 0 };
} else if (rawEventName === "onSubmit") {
e.preventDefault();
return { SEARCH: ref.current.value };
} else if (rawEventName === "onCancelClick") {
return { CANCEL_SEARCH: void 0 };
}
// Gallery
else if (rawEventName === "onGalleryClick") {
const item = e;
return { SELECT_PHOTO: item };
}
// Photo detail
else if (rawEventName === "onPhotoClick") {
return { EXIT_PHOTO: void 0 };
}
// System events
else if (rawEventName === "SEARCH_SUCCESS") {
const items = e;
return { SEARCH_SUCCESS: items };
} else if (rawEventName === "SEARCH_FAILURE") {
return { SEARCH_FAILURE: void 0 };
}return NO_INTENT;
}),
filter(x => x !== NO_INTENT),
),```
### Using effect handlers
In this example, we use two effect handlers, one to perform an API call, and the other one to perform rendering. We discuss the rendering in the next section. Effect handlers can only be called by command handlers, as command handlers are the only functions which receives the effect handlers as parameters. Here, our API call is a `fetchJsonp`:```js
export function runSearchQuery(query) {
const encodedQuery = encodeURIComponent(query);return fetchJsonp(
`https://api.flickr.com/services/feeds/photos_public.gne?lang=en-us&format=json&tags=${encodedQuery}`,
{ jsonpCallback: "jsoncallback" }
).then(res => res.json());
}
...
effectHandlers: {
runSearchQuery: runSearchQuery,
...
}
...
```Using effect handlers may seem overkill in some simple applications. In most cases, the only orchestration necessary is handling failing and successful effect execution with two different events. This is why effect handlers are optional. In more complex cases however, where the orchestration is complex, it will be useful to separate the orchestration of effects from the execution of effects. This is a tenet of functional programming: separating concerns enables easier testing, better composition and reuse.
### Customizing rendering
To customize rendering, we can override the default render handler. Rendering, as a matter of fact, is handled by an effect handler as any other effects. The effect handler for rendering can be configured on the key `[COMMAND_RENDER]`:```js
const flipping = new Flipping();
...
effectHandlers: {
...
[COMMAND_RENDER]: (machineComponent, renderWith, params, next) => {
// Applying flipping animations : read DOM before render, and flip after render
flipping.read();
machineComponent.setState(
{ render: React.createElement(renderWith, Object.assign({}, params, { next }), []) },
() => flipping.flip()
);
}
}
...
```The previous code shows how to customize the rendering to run a function just before rendering and just after rendering. The rendering itself is triggered with `React.setState` (you should not change that part). `React.setState` accepts as second argument a callback which runs immediately after rendering is performed. We may in the future hide away the details of `React.setState` so as not to expose implementation details in the interface.
### Customizing options
Here we simply change the initial event that trigger the display of the initial screen:```js
...
options: { initialEvent: [ "START"] },
...
```### Command handlers
Command handlers receive three parameters: the event emitter, the params passed by the command they handle, and the effect handlers passed to the machine factory. The orchestration here simply consists of dealing with failing and successful effect execution:```js
...
commandHandlers: {
[COMMAND_SEARCH]: (next, query, effectHandlers) => {
effectHandlers
.runSearchQuery(query)
.then(data => {
next(["SEARCH_SUCCESS",data.items]);
})
.catch(error => {
next(["SEARCH_FAILURE", void 0]);
});
}
},
...
};```
### A typical machine run
Alright, now let's leverage the example to explain what is going on here together with the `` semantics.First of all, we use `React.createElement` but you could just as well use jsx ``, that really is but an implementation detail. In our implementation we are mostly using core React API and [hyperscript](https://github.com/mlmorg/react-hyperscript) rather than jsx. Then keep in mind that when we write 'the machine', we refer to the state machine whose graph has been given previously. When we want to refer to the `Machine` React component, we will always specifically precise that.
Our state machine is basically a function which takes an input and returns outputs. The inputs received by the machine are meant to be mapped to events triggered by the user through the user interface. The outputs from the machine are commands representing what commands/effects to perform on the interfaced system(s). The mapping between user/system events and machine input is performed by `preprocessor`. The commands output by the machine are mapped to handlers gathered in `commandHandlers` so our `Machine` component knows how to run a command when it receives one.A run of the machine would then be like this :
- The machine will encapsulate the following properties as part of its extended state : `query`, `items`, `photo`. This extended state will be updated according to the machine specifications in function of the input received by the machine and the control state the machine is in.
- The initial extended state is `{ query: '', items: [], photo: undefined }`
- The machine transitions automatically from the initial state to the `start` control state.
- on doing so, it issues one command : render `GalleryApp`. Render commands have a default handler which renders the `renderWith` component passed as parameter with the *props* included in the render command. An event emitter (`next` in code sample above) is passed to allow for the element to send events to the state machine.
- The `Machine` component executes the render command and renders a gallery app with an empty query text input, no images(`items`), and no selected image (`photo`).
- The user enters some text in the text input
- The user clicks the `Search` button.
- A `submit` event is triggered.
- The value of the input field is read, and the `submit` event is transformed into a machine input `{SEARCH : }` which is passed to the machine
- The machine, per its specifications, outputs two commands : `COMMAND_SEARCH` and render `GalleryApp`, and transitions to `loading` control state
- The `Machine` component executes the two commands : the gallery is rendered (this time with a `Cancel` button appearing), and an API call is made. Depending on the eventual result of that API call, the command handler will trigger a `SEARCH_SUCCESS` or `SEARCH_FAILURE` event.
- The search is successful : the `SEARCH_SUCCESS` event is transformed into a machine input `{SEARCH_SUCCESS: items}`.
- The machine, per its specifications, updates its extended state `items` property, and outputs a render `GalleryApp` command. This displays the list of fetched items on the screen.
- Any further event will lead to the same sequence :
- the user or an interfaced system (network, etc.) triggers an event X,
- that event will be transformed into a machine input (as per `preprocessor`),
- the machine will, as per its specs, update its extended state and issue command(s)
- Issued commands will be executed by the `Machine` component, as per `commandHandlers`This is it! Whatever the machine passed as parameter to the `Machine` component, its behaviour will always be as described.
Note that this example is contrived for educational purposes:
- we could do away with the preprocessor and have the DOM event handlers directly produce inputs in the format accepted by the machine
- we could handle concurrency issues (user makes a second search while the first search request is in-flight) either reusing rxjs capabilities (`switchMap`) or at the machine level (extra piece of state)## Types
Types can be found in the [repository](https://github.com/brucou/react-state-driven/tree/master/types).## Contracts
- command handlers delegate **all effects on external systems** through the effect handler module
- the `COMMAND_RENDER` command is reserved and must not be used in the command handlers' specifications
- types contracts
- `next` is injected as a *prop* to the `renderWith` component and as such cannot be overriden by the component's defined *props*# Semantics
- The `` component:
- initializes the raw event source (event handler) which receives and forwards all raw events (user events and system events)
- creates a global command handler to dispatch to user-defined command handlers
- connects the raw event source to the preprocessor
- connects the preprocessor to the machine
- connects the machine to the command handler
- starts the machine: the machine is now reactive to raw events and computes the associated commands
- The preprocessor will receive raw events from two sources: the user interface and the external systems (databases, etc.). From raw events, it will compute inputs for the connected state machine. Note that:
- the preprocessor may perform effects only on the user interface (for instance `e => e.preventDefault()`)
- the preprocessor may have its own internal state
- The machine receives preprocessed events from the preprocessor and computes a set of commands to be executed
- The global command handler execute the incoming commands:
- if the command is a render command, the global handler executes directly the command in the context of the `` component
- if the command is not a render command, the global handler dispatches the command to the user-configured command handlers
- All command handlers are passed three arguments:
- an event emitter connected to the raw event source
- the parameters of the command they address
- an object of type `EffectHandlers` which contains any relevant dependencies needed to perform effects (that is the object passed in props to the `` component)
- Render commands leads to definition of React components with DOM event handlers. Those event handlers can pass their raw events (DOM events) to the machine thanks to the raw event source emitter
- Non-render commands leads to the execution of procedures which may be successful or fail. The command handler can pass back information to the machine thanks to the injected event emitter.
- The event handler must be an object with a **synchronous** `next` method (`Observer` interface) and optionally a `subscribe` method (`Observable` interface)
- The event source is terminated when the `` component is removed from the screen (`componentWillUnmount` lifecycle method)# Architecture
We are going all along to refer to a image search application example to illustrate our argumentation. Cf. [Example section](#example) for more details.In a traditional architecture, a simple scenario would be expressed as follows:
![image search basic scenario](assets/Image%20search%20scenario.png)
What we can derive from that is that the application is interfacing with other systems : the user
interface and what we call external systems (local storage, databases, etc.). The application
responsibility is to translate user actions on the user interface into commands on the external systems, execute those commands and deal with their result.In our proposed architecture with all options used, the same scenario would become:
![image search complete scenario](assets/Image%20search%20scenario%20with%20fsm.png)
In our proposed architecture with only mandatory options used, the same scenario would become:
![image search simplified scenario](assets/Image%20search%20scenario%20with%20mandatory%20options.png)
In that architecture, the application is refactored into a mediator, a preprocessor, a state
machine, a command handler, and an effect handler. The application is thus split into smaller parts
which address specific concerns :
- the preprocessor translates user interface events into inputs for the state machine
- the state machine computes the commands to execute as a result of its present and past inputs,
or, what is equivalent, its present input and current state
- the command handler interprets and executes incoming commands, delegating the execution of
effects to the effect handler when necessary
- the mediator orchestrates the user interface, the preprocessor, the state machine and the command
handlerWhile the architecture may appear more complex (isolating concerns means more parts), we have
reduced the complexity born from the interconnection between the parts.Concretely, we increased the testability of our implementation :
- the mediator algorithm is the same independently of the pieces it coordinates. This means it
can be written and tested once, then reused at will. This is our `` component. **This is
glue code that you do not have to write and test anymore**
- effect handlers are pretty generic pieces of code. An example could be code to fetch a
resource. That code is written and tested once (and comes generally tested out of the box), and
then reused for any resource. Additionally, only the effect handlers can perform effects on the
external systems, which helps testing, tracing and debugging[^3]
- effect handlers, being isolated in their own module, are easy to mock, without resorting to
a complex machinery specific to a testing library
- the state machine is a function which **performs no effects**, and whose output exclusively depends
on current state, and present input[^2]. We will use the term *causal* functions for such
functions, in reference to [causal systems](https://en.wikipedia.org/wiki/Causal_system), which
exhibit the same property[^1]. The causality property means state machines are a breeze
to reason about and test (well, not as much as pure functions, but infinitely better than
effectful functions)
- only the preprocessor and mediator can perform effects on the user interface, which helps
testing, tracing and debuggingWe also have achieved greater modularity: our parts are coupled only through their interface. For
instance, we use in our example below `Rxjs` for preprocessing events, and [`state-transducer`](https://github.com/brucou/state-transducer) as state machine library. We could easily switch to [`most`](https://github.com/cujojs/most) and [`xstate`](https://github.com/davidkpiano/xstate) if the need be, or to a barebone event emitter (like `emitonoff`) by simply building interface adapters.There are more benefits but this is not the place to go about them. Cf:
- [User interfaces as reactive systems](https://brucou.github.io/posts/user-interfaces-as-reactive-systems/)
- [Pure UI](https://rauchg.com/2015/pure-ui)
- [Pure UI control](https://medium.com/@asolove/pure-ui-control-ac8d1be97a8d)[^3]: Command handlers can only perform effects internally (for instance async. communication
with the mediator)
[^2]: In relation with state machines, it is the same to say that
an output depends exclusively on past and present inputs and that an output exclusively depends
on current state, and present input.
[^1]: Another term used elsewhere is *deterministic* functions, but we
found that term could be confusing.# Code examples
For the impatient ones, you can directly review the available demos:
| Code playground | Machine | Screenshot |
|:----|:----:|:----:|
|[TMDb movie search](https://codesandbox.io/s/ym8vpqm7m9)| ![graph](https://github.com/brucou/movie-search-app/raw/specs-all/article/movie%20search%20good%20fsm%20corrected%20flowchart%20no%20emphasis%20switchMap.png)| ![TMDb online interface screenshot](https://github.com/brucou/movie-search-app/raw/specs-all/article/app%20screenshot%20query%20detail%20-%20success.png)|
|[flickr image search](https://codesandbox.io/s/flickr-search-app-with-kingly-qivl3?file=/src/index.js)| ![](./assets/image%20gallery%20state%20cat.png)| ![image search interface](https://i.imgur.com/mDQQTX8.png?1) |# API design goals
We want to have an integration which is generic enough to accommodate a large set of use cases,
and specific enough to be able to take advantage as much as possible of the `React` ecosystem
and API. Unit-testing should ideally be based on the specifications of the behaviour of the
component rather than its implementation details, and leverage the automatic test generator of
the underlying `state-tranducer` library. In particular :- it should be seamless to use both controlled and uncontrolled components
- it should be possible to use without risk of interference standard React features like `Context`
- it should use the absolute minimum React features internally, in order to favor for instance a
painless port to React copycats (Preact, etc.)
- non-React functionalities should be coupled only through interfaces, allowing to use any
suitable implementation
- the specifics of the implementation should not impact testing (hooks, suspense, context, etc.)As a result of these design goals :
- we do not use React hooks, context, portal, fragments, `jsx`, and use the minimum React lifecycle
hooks
- the component user can of course use the whole extent of the API at disposal, those restrictions
only concern our implementation of the `` component.
- we defined interfaces for extended state updates (reducer interface), event processing
(observer and observable interfaces).
- any state machine implementation (including one that uses no dedicated library) can be
substituted to our library provided that it respects the machine interface and contracts:
- the machine is implemented by a function
- it takes an unique input parameter of the shape `{[event name]: event data}`
- it returns an array of commands
- it produces no effects
- we use dependency injection to pass the modules responsible for effects to the `` component# Tips and gotchas
- most of the time `preprocessor` will just change the name of the event. You can
perfectly if that makes sense, use `preprocessor : x => x` and directly pass on the raw
events to the machine as input. That is fine
- as long as the machine never has to perform an effect (this is one of the machine's contract)
. In our example, you will notice that we are doing `e.preventDefault()` in the preprocessor.
Furthermore, for documentation and design purposes, it makes sense to use any input
nomenclature which links to the domain rather than the user interface. As we have seen, what is
a **button click** on the interface is a **search input** for the machine, and results in a
**search command** to the command handler.
- if the machine at hand is only designed for that user interface and not intended to be reused
in any other context. This approach as a matter of fact couple the view to the machine. In the
case of our image gallery component, we could imagine a reusable parameterizable machine which
implements the behaviour of a generic search input. Having a preprocessor enables to integrate
such machines without a hiccup.
- some machine inputs may correspond to the aggregation of several events (in advanced usage). For
instance, if we had to recreate a double click for the `Search` button, we would have to receive
two clicks before passing a `SEARCH` input to the machine. Having an `eventHandler` interface
allows to use `Rxjs` to deal with those cases, as its combinator library (`map`, `filter`,
`takeUntil` etc.) allow to aggregate events in a fairly simple manner. Note that we could
implement this logic in the state machine itself (our machines are essentially Turing machines, they can implement any effect-less computation), but:
1. it may be better to keep the machine dealing with inputs at a consistent level of
abstraction; 2. that kind of event aggregation is done easily enough with a dedicated
library such as `rxjs`
- you may want to handle some concurrency issues at the machine level. Typically in our
example, that would mean handling the user scenario when the user is requesting two
different queries in rapid succession and the first query response has not arrived before the
second query is executed. There is in this case a risk of the user interface displaying the wrong
response.
- you may also want to do it at the command handler level to keep your machine at a higher level
of abstraction. A command handler may for instance recreate Rxjs's `switchMap` by keeping a record
of in-flight queries.
- the interfaced systems can communicate with the machine via an event emitter. The
`props.renderWith` React component is injected a `next` *prop* which is an event emitter which
relays events to the machine's raw event source. Associated with DOM event handlers, this allows
the machine to receive DOM events. Command handlers are also passed the `next` event emitter, and
can use it to send to the machine any messages from the interfaced systems.
- in those cases where the machine needs to communicate with other local but out of scope entities,
it can emit its own events, for instance custom DOM events# Prior art and useful references
- [User interfaces as reactive systems](https://brucou.github.io/posts/user-interfaces-as-reactive-systems/)