React-style state machine for Framer

Andrew Liebchen
Framer
Published in
4 min readDec 27, 2016

Managing simple state in Framer prototypes is pretty straightforward. Let’s say we’ve got a counter, a message that tells you the state of the counter, and a button that increments the counter. Simple: just set the initial value of the counter variable, set up the message, increment the variable onClick, then update the message.

counter = 0message = new Layer
html: "The button has been pressed #{counter} times"
button = new Layer
...
button.onClick ->
counter++
message.html = "The button has been pressed #{counter} times"

The counter variable above stores a state. Framer allows you to store state directly on layers. Since layers are structured like objects, state is just a object of the layer that has a name and a bunch of properties that the layer displays when that state is active.

button.states.disabled =
opacity: 0
ignoreEvents: true
button.stateSwitch('disabled')

It’s complicated

Cool, we can store state in variables and layers can have states themselves. As you can see above it’s easy to define and set state on layers. This works great for simple prototypes and interactions, but what happens when our prototypes get more complex? What if we have to change a bunch of layers’ state in response to an action?

button.onClick ->
foo.stateCycle()
bar.stateCycle()
bat.stateCycle()
baz.stateCycle()

Not too bad, but things get a little more crazy when need to bind multiple state changes to multiple layers. You can manage it with a loop, perhaps…

toggles = [button, closeButton, openButton, toggleButton]for toggle in toggles
toggle.onClick ->
foo.stateCycle()
bar.stateCycle()
bat.stateCycle()
baz.stateCycle()

…which works fine, but having to define the state of every layer that needs to respond to a change of application state gets old fast. There’s got got be a better way.

What React Does

ReactJS, unlike Framer, has got a neat way of dealing with application state: it stores it all in one place and then re-renders the necessary parts of the UI when the state changes. Combined with a well-defined inheritance model to pass information around the component tree, React makes it really easy to “reason” about state. All your application’s state is in one place, and how components respond to that state are written in the components themselves.

class Counter extends React.component {
constructor(props) {
super(props);
this.state = {
counter: 0
}
}
render() {
return (
<div>
<p>The button has been pressed {this.state.counter} times</p>
<button onClick={this.handleClick}>
Add one
</button>
</div>
);
}
handleClick() {
this.setState({counter: this.state.counter++});
}
}

If you’re not familiar with React or JS classes, this might look intimidating. But if you look closely, you can probably figure out what’s going on. The counter state is initially stored in the constructor as 0. It’s rendered in the message within the render function. When the button is clicked, handleClick increments the state.

This alone is neat, but the real magic is hidden in React’s source code. When we increment that counter, any part of the component that relies on the state (in our case, the message) is automatically re-rendered to reflect the change.

How to make it work in Framer

The basic functionality we need is the ability to:

  1. Define and update an object that holds application state
  2. When the state changes, re-render affected layers

This turned out to be pretty simple to build a module that could do just this. Based on an idea from Kevyn, the modules has three basic parts: a function that contains all your layers, a state object, and another function that allows you to easily update your state.

Here’s an example using my module:

# Import our module
{ Machine } = require 'stateMachine'
# Define the initial state
state =
counter: 0
# Wrap application layers and events in a function
layers = () ->
message = new Layer
html: "The button has been pressed #{state.counter} times"
...
button = new Layer
...
# Increment the counter when the button is pressed
button.onClick ->
app.setState('counter', state.counter++)
# Create an instance of the module
app = new Machine
state: state
layers: layers
framer: Framer

And here’s a simple todo app using this module:

Try it yourself or view the basic source code.

What’s next?

This is a good start, but it’s got some problems. For one, every time the state is changed the entire app must be re-rendered. When the application is complicated this could slow Framer down considerably, and fail for user changes stored in mechanisms other than state, such as draggable layers.

One of React’s ingenious features is that for any given change, it only re-renders the parts of the UI that actually need to change. This is possible for our Framer module, but it will require additional work. I’m not the most sophisticated programer, so I limited the scope of this first project.

Other than the basic todo app, I haven’t had a chance to try this module out on a larger prototype. I’ll let you know how that goes.

Update

I’ve used the module in a few generative prototypes, and it works pretty well. I’ve improved the API a little bit, to make it easier when you simply need to toggle the state (something I do a lot) and allow setState to accept more than just one key/value pair.

Grab the module on Github!

--

--

Andrew Liebchen
Framer
Writer for

Currently, a Product Designer at Facebook. Formerly, an architecture student. Formerly formerly, a theatrical set designer.