Introducing Halogen Store: Global state management for Halogen

I’d like to announce halogen-store, a state management library for Halogen applications. It’s used as the state management solution for Real World Halogen.

I put this library together because folks regularly ask about global / central state management in Halogen applications on Slack, here on Discourse, and elsewhere. It’s easy enough to advise a ReaderT + Aff pattern, but it’s nicer to have a library that handles the details for you. And that’s what halogen-store is for!

The library is minimal with room to grow, but it’s usable right now. The approach is loosely modeled after Redux and the Elm architecture, in which you dispatch messages to a central store and a reducer function applies the state update.

I hope y’all enjoy, and please let me know on the issue tracker if you have questions or run into issues!

16 Likes

How has the performance looked to you @thomashoneyman?

I haven’t done any serious performance testing on the library, so I can’t provide any precise measurements. That said, the primary performance issue will be excessive re-rendering of connected components, and the library has guards in place to prevent this from happening.

The big issue is that changes to the global state will cause renders in any connected component, just the same way that changes to local state will cause a component to re-render. This can get out of hand: if a parent component and child component are both connected to the global state, then a state change will cause the parent to re-render (which will cause the child to re-render), and then cause the child to re-render. So you can end up with quadratic rendering.

The library has two checks to prevent this from happening.

First, you are required to use a Selector when connecting a component to the global state. The selector pulls out just the sub-part of the global state that you need for your component, and it contains a memo function that will ensure your component is only notified of state changes when that sub-part of state has changed. In other words, the component no longer re-renders on any global state change: it only re-renders when the state it cares about changes.

Second, the connect higher-order component uses a version of the lazy function from Halogen to only re-render its child component (the connected component) when its input or connected state has changed. This is another small help to prevent unnecessary rendering.

To summarize, I don’t have hard numbers on the performance impact of using this library vs. using no global state at all, but I think that the library will be more performant than a typical ReaderT + Aff setup that doesn’t keep some of these factors in mind.

6 Likes