What is Observable State?

The Untapped Composable State Solution for React

Brad Lemley
9 min readSep 16, 2019

What is Observable State?

Observable state is the observer pattern applied to state. It means that listeners can subscribe to state, receiving the new state whenever state changes.

It’s easy to imagine how observable state might be useful in a State-View framework such as React — the view can subscribe to state and update whenever it receives a new state.

What may not be so obvious is how observable state enables reactive and composable state, and just how powerful reactive and composable state are.

Reactive and Composable State

When all entities in the system support the same observable state mechanism, the entities can be connected to each other in arbitrary ways. Each entity receives state from upstream and emits state downstream. The following diagram demonstrates:

Observable State System

The result is both a reactive and composable system. Reactive because state emitted from the first entity results in a chain reaction, each entity acting on the state it receives from the upstream entity. Composable because each entity takes state as input and outputs state, the output being useable as the input to another entity.

Note, in the diagram, the green symbols represent an observable interface, e.g. subscribe, and the arrow represents direction of flow. Downstream entities subscribe to upstream entities and state flows the opposite direction.

So, how is this useful for an app? Let’s take a closer look...

Applying Observable State

Entity 1 is a producer of state, a source. It emits state whenever its state changes. What exactly does this represent in an app? It could be a lot of things. Let’s put this off for now.

Entity 4 consumes state, a sink. It represents a view. We already discussed how observable state works nicely with view frameworks because the view can subscribe to state and re-render whenever it gets a new state.

In between are Entities 2 and 3 which receive state from upstream, operate on it, and emit state downstream — known as reactive operators. They transform state. They can derive state from other state or rename pieces of state, but they don’t produce state. Their purpose is to abstract the source state from the sink state.

If you’ve used Redux, you’re probably familiar with selectors and mapStateToProps — they serve the same purpose, sitting in between the Redux store’s state and components, operating on state before it gets to components.

Producing State

Now, let’s get back to Entity 1, the state source. If you’re familiar with Redux, you might be thinking that a Redux store is a source of state. Indeed, it could be. A Redux store does have a subscribe method and emits state every time its state changes. Note that state changes are caused by Redux actions, the “inputs”.

Thinking about a Redux store is a good way to conceptualize a state source, but there are much easier ways to create state sources. An observable state system itself naturally supports all of the functionality of Redux — IMO, in a cleaner, more modular way — so, there’s not much reason to use both unless you’re transitioning from a legacy Redux system.

It’s important to recognize that the state source is the functionality of the system. Other entities in the system just transform state or consume state, they don’t produce state or change state (transforming state is different than changing state). All of the functionality in the system is inside the state source.

The key to a clean and modular system is the ability to have multiple independent blocks of functionality — modules. That describes an ideal software system, no matter what the purpose. For an observable state system, this means having multiple state sources. In order to achieve this, we need to introduce some new capability to our reactive operators: the ability to take multiple observable state inputs. The following diagram demonstrates what such a system looks like:

Observable State System with Multiple State Sources

Entity 3 takes multiple observable state inputs, but still only outputs a single state — a combined state — and that’s what allows it to fit in as just another entity in the system. How does it work? The combiner (Entity 3) saves the latest state from each of its inputs. When it receives a new state from one of its inputs, it combines it with the latest (saved) states from the other inputs.

Notice that the inputs to Entity 3 don’t have to come directly from sources, they can go through some other reactive operators first. This is the beauty of a composable system — entities can be inserted anywhere they are needed.

Also note that Entity 4 is not aware that multiple state sources exist—this is abstracted by the reactive operator entities, it all just looks like one source to the view, no matter how many sources actually contribute. Again, the beauty of a composable system.

Similarly, notice that each entity could internally be made up of other entities— sort of like a fractal, as you zoom in to each entity, you could see more entities. Did I mention that the composable state system is beautiful?

So, how do we create a state source?

Stated Libraries

One way to implement sources is an approach I’m calling Stated Libraries. A Stated Library is an object that implements some functionality, maintains some internal state, and “outputs” the state via observable state. The “inputs” to the library are regular object methods. A Stated Library is just a regular javascript object with methods used to invoke functionality; the only difference is that the methods (generally) don’t return anything…the object methods only serve to change state, which is then emitted to listeners.

Here’s a demonstration of a Todo library as a Stated Library:

const todoLib = createTodoLib();todoLib.subscribe(state => console.log(JSON.stringify(state));
// out: {todos: []} <-- current state emitted upon subscribe
todoLib.addTodo('Learn Stated Libs');
// out: {todos: [{title: 'Learn ObState', done: false, id: 1}]}
todoLib.completeTodo(1);
// out: {todos: [{title: 'Learn ObState', done: true, id: 1}]}

You might imagine that the implementation of such a library is fairly straight forward, and indeed it is. Stated Libraries provides some helpers to make it super easy. Stated Libraries are standalone modules, developed and tested independently. Stated Libraries can perform async functionality and cause side effects normally. There is no middleware, no boilerplate.

Multiple Source Example

To demonstrate multiple state sources, imagine the Todo app needs to support a todos visibility filter. The visibility filter state could be implemented as a separate Stated Library (visLib) with a reactive operator used to derive visible todos. The architecture might look like this:

Todo App Architecture w/ Stated Libraries

Stated Libraries’ mapState reactive operator is used to combine the states from the two sources and derive an additional piece of state (visibleTodos). How easy is it to implement this? The code looks like this:

const composedState = mapState(
[todoLib, visLib],
([todoState, visState]) => ({
...todoState,
...visState,
visibleTodos: visState.filter === 'active'
? todoState.todos.filter(todo => !todo.done)
: todoState.todos
})
);

The mapState operator is probably the only operator you’ll ever need for composing state and it is very easy to use. It’s important to note that mapState is actually an operator factory — it creates an operator entity, the entity shown in the diagram. The following demonstrates how composedState itself is an observable state, and how calling a visLib method (setFilter) results in a change to composedState.

composedState.subscribe(state => console.log(JSON.stringify(state))
// console output:
// {
// todos: [
// {'First', done: true, id: 1}
// {'Second', done: false, id: 2}
// ],
// filter: 'all',
// visibleTodos: [
// {'First', done: true, id: 1}
// {'Second', done: false, id: 2}
// ],
// }
visLib.setFilter('active');
// console output:
// {
// todos: [
// {'First', done: true, id: 1}
// {'Second', done: false, id: 2}
// ],
// filter: 'active',
// visibleTodos: [
// {'Second', done: false, id: 2}
// ],
// }

Hopefully it’s clear that this composedState object could be used as the input to another mapState operator. BTW, it is totally possible to create your own operator or operator factory, but probably won’t ever be necessary.

That’s the basics of Stated Libraries — it‘s a way to create state sources, and combine and transform states from multiple sources. But, there’s a lot more interesting features, for example:

  • Stated Libraries support time-travel debugging
  • Stated Libraries can implement async functionality normally
  • Stated Libraries are completely independent modules which is great for distributed development, testing, and packaging
  • Stated Libraries have no global store, no actions, no reducers, no middleware
  • Global-level application functionality can be implemented and tested in its entirety without any app framework or views.

If your interest is piqued, go have a look at the Stated Library docs.

Reactive Programming

Most of the concepts described in this article are reactive programming concepts — the concepts of RxJS applied to state. In fact, mapState functionality could be implemented with a few RxJS operators (combineLatest and map). Any entity with the observable interface is essentially an RxJS observable. Terminology-wise, I prefer the terminology “operator factory” which creates an “operator” entity whereas RxJS calls an operator factory an operator. Also, I find RxJS’s definition of an “Observable” to be confusing and intimidating, so I’m trying to avoid the term and instead say something like “observable state”. Stated Libraries are not implemented with RxJS, but they are compatible with RxJS. If you’ve understood the concepts discussed in this article, you’re most of the way toward understanding RxJS and reactive programming. The main contributions of Stated Libraries is how to create state sources of functionality and how to apply the reactive programming concepts to state.

Observable State vs React Hooks

Actually, observable state and React hooks aren’t all that different. A Todo library could be written like this with React hooks:

const useTodoLib = (initialState) => {
const [state, setState] = useState(initialState || { todos: [] });
return {
addTodo(title) {
setState({
...state,
todos: state.todos.concat({title, done: false})
})
},
...state,
}
}

…or like this with Stated Libraries:

const createTodoLib = (initialState) => createStatedLib(
initialState || { todos: [] },
base => {
addTodo(title) {
base.updateState({
todos: base.state.todos.concat({title, done: false})
})
},
}
);

Compared to React Class Component’s setState , hooks are clearly superior for this sort of thing…but, are hooks really that great? I don’t know. The fact that useTodoLibrary gets called every time the component renders is both very interesting and very weird. For life-cycle-related functionality, hooks are great because sometimes that’s exactly what you need — for functionality to ‘re-function’. But for global functionality like the Todo example…implementing functionality in React-specific hooks paradigm is exceedingly strange. Observables is a fairly well established reactive programming model. Will the React hooks reactivity model become the new standard for reactive programming? Will it be used outside of React? (Well, yes — Vue is already adopting something similar.) Will it be used outside of app frameworks? Dunno.

I’m continually looking at React hooks examples and trying to implement them with observable state (Stated Libraries). Sometimes local component state functionality is similar to the Todo library example — but life-cycles, changing props, and DOM references definitely add challenges. At this point, I’m using observable state (Stated Libraries) primarily for global application functionality and state…still developing strategies for using observable state for more local functionality.

Other App Frameworks

Observable state is a generic concept; Stated Libraries is also generic and is completely independent of any application framework. Functionality implemented with Stated Libraries could potentially be used with other frameworks, but bindings currently only exist for React.

Whenever I search for observable-related things, Angular’s observables always come up. Angular uses Observables somehow, but it seems more geared toward handling events and messages than state. If you know of some other usage, especially usage similar to observable state concepts discussed in this article, please let me know in the comments.

--

--