When writing applications with react you will soon reach a point where you pass down callback functions as props two or three levels, to change the parents state. For most projects you will need a third party solution for global state management, so you can directly access and modify state from any component in your app, without passing callbacks down the DOM.
Redux has somewhat become the industry standard. However, it includes quite some boilerplate for every simple state change. Although I have to admit the situation feels better now with the introduction of hooks, it is still hard for beginners to wrap their head around the concepts. During the learning experience the question comes up: Why would we do it like this? Isn't there a simpler way? There actually is. More on that later.
I don't want to bash redux here. It is industry leader for a reason and has its strengths. But once you have worked with Vue and Vuex, most people realise it just feels much more intuitive. This is just one of the reasons why vue is growing so rapidly and just recently got more stars than react on github.
So before we go over some of the alternatives, let's first take a step back and have a look at what redux does and what it is good for.
Redux is a global state management solution for frontend applications based around the idea of reducers. Reducers are pure functions, which take the previous state and action object, containing a type and an payload, to mutate the state. The action objects are also pure functions. This is already one thing, where many newcomers are scratching their heads.
Without going more into detail here, this approach has two main advantages.
You can now test your state completely isolated from your components. You import the reducer and the action into your test file. Since they are pure functions they have no dependencies whatsoever. You can than define a initial state object and pass it to the reducer, together with an action object. This way you can verify that a certain action mutates the state correctly without ever mounting a component. Since the state logic is tested already, all you have to do in the components, is to ensure that certain actions trigger the dispatch function with the correct action object. The dispatch function does not really have to executed and can be mocked.
Since all your state mutations pass through a reducer and the passed action object contains information about what has to be changed and how, it is easy to track state changes. If you would mutate state directly and at some point wonder, why the state looks like it looks, you will have a hard time debugging, since the change could come from anywhere. With redux, you can move the debugger or logger into the reducer and observe each and every action of the state.
So redux definitely has it's advantages and especially shines in large scale projects where many programmers work on the same codebase and where we have big, deeply nested states.
Still, having to add an action function, modify the reducer and then connecting the component to toggle a boolean value, just does not feel natural and especially for small scale projects and prototypes probably is just overkill. But also for large scale projects there are alternatives. Let's have a look.
ReactN is a extension of React that includes global state management. It treats global state as if it were built into React itself -- without the boilerplate of third party libraries.
Damn that sounds amazing. Initializing the global state works as follows:
With reactn you can simply refer to
this.global instead of
this.setGlobal instead of
this.setState and you are done.
setGlobal can even handle async data.
Using hooks? No Problem.
For most people this approach should be much easier to handle, since it mirrors reacts default behaviour.
With reactn you can even use redux developer tools. Depending on your state, your app probably re-renders too often. But even then you can make use of
withGlobals HOC and fallback to a redux like approach, similar to
mapStateToProps. But you can do it where and when it is needed.
Pullstate has similar syntax. However, it has no support for class components. It only delivers a third party hook for global state management.
You can define as many stores as you want and they would be completely seperate.
To access values from the state, you call
useState on your imported store and pass a reducer function as callback. The
s here is the complete store and you simply return the value, you want to access.
How could I not mention Mobx here? The previous libraries where quite small and you do not know for sure for how long they are maintained. However, they also have quite small footprint and for most projects they will speed up initial development time a lot. However, if you want an actively maintained third party state management library, which is ready for large scale production apps and still want to avoid using redux, mobx is one of very few options.
So with Mobx you basically depend on other third party libs, like
mobx-react-lite, similar to 'react-redux'. You have to wrap your components in a
observer function and use
useObservable to initialise. Afterwards you can modify the store directly to trigger re-renders. To make it global you would have to define the store outside of the components and pass it into it via props.