Creating global state in ReasonReact

Kyle Henderson
4 min readJan 21, 2018

--

ReasonReact by default provides a solution for managing stateful components by using reducer components. Reducer components are wonderful for managing small segments of state throughout your system but run into some serious issues in a larger application. Having a more persistent global state that uses the reducer architecture and elevates state out of the component will provide the best of both worlds.

Setting up a basic reducer component

ReasonReact reducer components are the basic way to manage state within a ReasonReact application. They work through having a state, a set of actions and a reducer.

  • state: the current state of the component
  • action: a set of actions that can modify state
  • reducer: a function that takes an action and a state to compute a new state.
A counter using ReasonReact

This component is a simple counter, it mutates through the reducer component architecture.

  1. A user clicks on the “+” button, an Increment action is sent out
  2. The reducer is fired with the action and current state
  3. The reducer returns a ReasonReact.Update with the new state

And that is it, that (other than background wiring) is how this reducer component goes through mutating itself.

A bit more complex example is that of a todo list.

All though there is a lot of code in this example, the system is actually rather simple. All the operations are actions, they are mapped to simple state changes by the reducer. If you are at all confused I would recommend walking through the life cycle of an action, the parts should make sense.

I strongly recommend reading the docs, they go into detail explaining the different kinds of updates and more interesting ways to interact with reducer components. https://reasonml.github.io/reason-react/docs/en/state-actions-reducer.html

Drawbacks to reducer components

The biggest issue with the examples above is that the state is inherently bound to the component. For example, if you remove the component, the state is lost and if you lose state the component just restarts from its initial state. This ephemeral state becomes a huge problem when dealing with problems like routing, because there no way to maintain your state between URL changes.

How do you fix this

Global state, by moving the state out of these ephemeral components we can create an application that can easily handle being deleted and recreated.

How? simple, a reducer component! :P

The architecture above can be moved up the component tree, by creating a parent component that handles state and passes down only what is needed we get the best of both worlds. The reducer component model and state separated from ephemeral components.

This does require a tiny modification to the original components.

** if you want to see the todo component it is in the source linked at the end and the application is hosted at http://hehk.github.io/example-reason-react-global-state**

As you can see the changes are very minimal, all I did was make a parent component with a union of the sub-states and give it the responsibility of updating that new state. We also just change over a few function calls in the original components to use the new dispatch function and we are golden.

It works!!

Benefits

  1. This decouples the state from the components, you do not actually need the reducers and initial state to be in the same module. They could be completely different and handled differently. For example, you could create a language module with state, actions and reducers but have no specific language component, and its state would just be passed down to the components
  2. You can now persist through deleting and recreating elements

Problems with this method

  1. This solution is rather boilerplatey and could get ugly fast.
  2. You have to pass state explicitly into the components through props. ** You could create an observable around the state and have all the sub-components subscribe as one possible workaround. **
  3. Any change other than generic update would require a rewrite, currently the sub-reducers only return state. You could make them return a tuple with state and update type but that would also just be a ton more boilerplate.

I expect someone far better at Reason/OCaml will eventually come out with an elegant solution that blows this code’s socks off but, for right now, I think this is a simple and easy way to create a global state.

Closing Notes

Thanks for reading, I am new to writing blog posts, so hopefully it was not horrible. I wrote this because I am a big fan of working examples and I could not find anything on more interesting state management. If you think anything was wrong or could be better explained, PLEASE tell me.

If you want to learn more about using the reducer components I would checking out the docs at https://reasonml.github.io/reason-react/docs/en/state-actions-reducer.html

All the code for this post come from a sample app at: https://github.com/Hehk/example-reason-react-global-state

--

--