Anyone who’s developed front-end applications knows that managing the state is one of the most important, and challenging, aspects. Popular component-based view libraries, like React, include fully functional, if not rudimentary, state management. They enable each component of your application to manage their own state. They work well enough for small applications, but you’ll quickly encounter frustration. It’s a challenge deciding which components have state and how the data from each state gets shared between components. Then there’s figuring out how or why state was changed.
To address the above problems of component-oriented state, libraries like Redux were introduced. They centralize that state into a centralized “store” that every component can read and write to. To maintain order they centralize the logic that changes the state into a central part of the application called the reducer, which is invoked using actions, and causing it to produce a fresh copy of the state. It’s very effective but has a high learning curve, requires a lot of boiler-plate, and forces you to separate code that updates the state from the code that renders the view.
SpaceAce is a new library that takes all the benefits of Redux, such as a centralized store, immutable state, unidirectional data flow, clearly defined actions, and it greatly simplifies how your code updates the store’s state.
We’ve been using SpaceAce to manage state in our main React app at Trusted Health for nearly a year now with great success. While our engineering team is relatively small (3 people) it has really sped up feature development without sacrificing code complexity or testability.
SpaceAce provides a state management store known as a space. A space is the read-only (immutable) state and a toolkit for updating it. But this store doesn’t just have state inside it, it is the state. At the same time, it provides multiple methods for generating new version of state. How? It’s a function… with properties! Many JS developers don’t know that JS functions are also objects. In addition to being executable, they can also have properties, just like objects (because they’re objects too!).
Each space is an immutable object with properties that can be read directly, but cannot be changed directly. Each space is also a function that can create a new copy with specified changes applied to the state.
Finally, an example:
The above example shows how you make a space and directly “change” it by calling it with an object to merge onto the state. This is similar to React’s setState, applying a shallow merge. Keep in mind though that the original space doesn’t change, it instead returns a copy with the changes applied.
However, it’s not really useful until the application re-renders with the new state. To make that easier, a subscribe function is provided. It invokes a callback whenever the associated space “changes”:
Most of the time, the state changes due to something the user has done. They click a checkbox, select an option from a dropdown, or type into a field. SpaceAce makes updating state from these simple interactions ridiculously easy. If you invoke the space with a string, it generates and returns a handler function:
While most apps contain a lot of simple interactions, they also consist of complex actions. SpaceAce allows you to define custom actions, all within the same file as your component. When called, these actions are given an object filled with handy functions for updating the state:
You may notice that all these ways of changing a space’s state assumes that the state is relatively shallow, but how is that possible if there’s only one space per app? It isn’t! Every space can have any number of child sub-spaces, which are also just spaces, but they have parents. Whenever one of those child spaces is updated the changes bubble up, triggering a re-render of the app once the change reaches the root space.
The best part of child spaces, is you don’t need to explicitly make them. They’re automatically created when you access an object on the space:
There’re a bunch more features to discover, and fun tricks that I’ll be sharing soon. Stay tuned for my next post!