Intuitive State Management with Reactive Magic

Chet Corcos
4 min readJul 23, 2017

I’ve spent a lot of time the last couple years learning and exploring the depths of functional programming. But recently, I’ve taken a step back and thought a lot about mental models. I was inspired by the article Thought as a Technology, specifically about how there is sometimes a huge disconnect between the way we think about a problem and the way we represent it. I think functional programming is a perfect example of this disconnect. It takes a lot of time and effort to learn a mental representation of functional programming concepts before you can proficiently understand a reasonably complicated Haskell program. This disconnect is the reason that I keep going back and re-reading Functors, Applicatives, And Monads In Pictures. In a recent project, I tried to re-think the way I build web applications. Rather than try to learn a mental model for a new architecture, I tried to create an architecture that represents the mental model I already have for how a program should work. The result is this library I call Reactive Magic.

Reactive Magic is really simple. You can create a value using the Value constructor, and you can .get() and .set() the value as you might expect.

import { Value } from 'reactive-magic'
const x = new Value(1)

console.log(x.get())
// => 1
x.set(2)
console.log(x.get())
// => 2

This value is more than just a variable though. It’s ✨reactive✨. For example, you can create a DerivedValue which will re-evaluate the function you pass it anytime you .set() a value this function depends on.

import { Value, DerivedValue } from 'reactive-magic'
const x = new Value(1)
const y = new Value(1)
const z = new DerivedValue(() => x.get() + y.get())

console.log(z.get())
// => 2
x.set(10)
console.log(z.get())
// => 11

This all works due to a clever trick I learned from Meteor.js circa 2014. Because JavaScript is single-threaded, you can maintain a global variable that references the current function that is evaluating. And any time you .get() a reactive Value you can register a listener to re-evaluate that function anytime you decide to .set() that reactive Value . It’s actually much simpler than you might imagine. I implemented all of this in 118 lines of Typescript on top of a simple and elegant observable streams library called flyd. In fact, this entire library (including flyd) is just 2465 bytes gzipped.

So how might you actually use this? I’ve created a simple Component that wraps a React component which listens for reactive values. You have to use the view() function instead of render() but it works pretty much as you might expect.

import * as React from "react"
import { Value } from "reactive-magic"
import Component from "reactive-magic/component"

interface CounterProps {}

class Counter extends Component<CounterProps> {
count = new Value(0)

increment = () => {
this.count.update(count => count + 1)
}

decrement = () => {
this.count.update(count => count - 1)
}

view() {
return (
<div>
<button onClick={this.decrement}>{"-"}</button>
<span>{this.count.get()}</span>
<button onClick={this.increment}>{"+"}</button>
</div>
)
}
}

This abstraction works really well when you want to start building more complicated interactions. For example, suppose we want to have two counters where one counter determines the amount by which the other counter increments or decrements. I call this the “delta counter” example and I often use this contrived example to gain insight into the abstraction power of a certain architecture. Anyways, here’s how I’ve redefined the counter component.

interface CounterProps {
count?: Value<number>
delta?: Value<number>
}

class Counter extends Component<CounterProps> {
count: Value<number>
delta: Value<number>

constructor(props) {
super(props)
this.count = props.count || new Value(0)
this.delta = props.delta || new Value(1)
}

increment = () => {
this.count.update(count => count + this.delta.get())
}

decrement = () => {
this.count.update(count => count - this.delta.get())
}

view() {
return (
<div>
<button onClick={this.decrement}>{"-"}</button>
<span>{this.count.get()}</span>
<button onClick={this.increment}>{"+"}</button>
</div>
)
}
}

Now for the delta counter, all we need to do is instantiate the reactive value that needs to be shared between these two counters and pass them on to the appropriate props.

interface DeltaCounterProps {}

class DeltaCounter extends Component<DeltaCounterProps> {
delta = new Value(1)

view() {
return (
<div>
<div>
delta: <Counter count={this.delta} />
</div>
<div>
count: <Counter delta={this.delta} />
</div>
</div>
)
}
}

I really enjoy the simplicity of this pattern. I don’t know about you but it feels very intuitive to me. And better yet, it’s performant right out of the box. You don’t need to worry about diff’ing props and unnecessary re-renders. It all just works.

As I’ve used Reactive Magic in a few increasingly complex projects, I have found that it does a very good job at managing shared global state. In a pure functional architecture, you might find yourself painfully threading a global value or callback function through many layers of components to get that value to the button that needs it. But in Reactive Magic, you can just .get() and .set() a global variable wherever you want and everything just works.

const World = {
sidebarOpen: new Value(false),
}

class ToggleSidebarButton extends Component<{}> {
private toggleSidebar() {
World.sidebarOpen.update(bool => !bool)
}

view() {
return (
<button onClick={this.toggleSidebar}>
toggle sidebar
</button>
)
}
}

class Layout extends Component<{}> {
view() {
<div>
<Sidebar open={World.sidebarOpen.get()}/>
<Content/>
</div>
}
}

I’ve been pleasantly surprised by how much I enjoy using Reactive Magic. It is unapologetically impure, and yet it’s concise and does a very good job at representing how I think when I’m building a user interface.

Give it a whirl and let me know what you think! https://github.com/ccorcos/reactive-magic

--

--