No-Boilerplate Global State Management in React

Seems like a pile of hot garbage to me.

”“Global state packages are very cumbersome and complex to set up.

They violate the KISS principle — Keep It Simple, Stupid.

”After this list, I feel the need to reiterate: I am a fan of Redux, and I use it on my personal projects.

The purpose of this article is not to trash Redux or MobX, or to propose that they are flawed systems.

It is to highlight a real issue: There is difficulty integrating these packages into real applications.

Most of this difficulty seems to stem from the learning curve.

These packages are “too clever” and are not as accessible to the junior developers who tend to make up the majority of contributors to projects.

One piece of feedback I received explicitly blamed the packages’ users: “Users don’t put enough effort into evaluating their needs; don’t use [the packages] judiciously or as advised; don’t give a second thought to any dependencies they add; and never revisit their design decision, then complain about them.

”I think they were onto something.

I don’t think Redux or MobX are innately flawed, but I think there is a real difficulty in integrating them into enterprise projects.

They may not be the best solution, not out of function, but out of complexity.

I am hoping with the release of React 16.

7 Hooks and its re-conceptualization of how a readable React application looks, we will see global state solutions that harness creative methods that appeal to wider audiences.

With the ultimate goal of no boilerplate and intuitive syntax, this article will offer my opinion on how a global state management system for React can be structured.

I‘ve also included my open-source attempt of that implementation.

You may use this implementation yourself via reactn on NPM or contribute to, fork, or otherwise spy on the open-source GitHub repository.

Keep It Simple, StupidAn intuitive approachMy personal take on the matter is that global state management systems appear to be designed with global state management in mind, not React.

They are designed so broadly that the intention is to be usable even outside of React projects.

That’s not a bad thing, but it is unintuitive for junior developers who may already be overwhelmed by learning React.

React has state management built-in — this.

state, this.

setState, and the new useState hook.

I posit that global state management should be as simple as local state management.

The migration to or from the global state should not require an entirely new skill set.

We read from and write to a local component state using the following syntax:We should be able to harness the power of global state similarly:Each property on the global member variable this.

global can harness a getter that subscribes the component instance to property changes in the global store.

Whenever that property changes, any instance accessed gets re-rendered.

Updating property name in the global store does not re-render a component that only accesses property this.

global.

age.

But it does re-render components that access this.

global.

name, as would be intuitive behavior of a state change.

As a technical necessity, a global hook would need the property name (instead of a default value) in order to access that specific property.

I would opt away from a default value on a global hook.

Almost by definition, a global state property is made to be accessed by multiple components.

Having to put a default on each component, which should theoretically be the same default value for all instances of that property, is not DRY code.

Global defaults should be managed externally, such as an initializer.

And if you want the entire global state object in a hook:Though a functional component, global would be analogous to this.

global and setGlobal would be analogous to this.

setGlobal in a class component.

No BoilerplateMinimal setup or modificationWhen we strip a lot of the features of Redux or MobX that developers find unnecessary, tedious, or otherwise superfluous, there isn’t much boilerplate needed.

Especially when we gear our package towards React itself and not on a global state solution for the Internet as a whole.

If we want this.

global and this.

setGlobal in class components, then it needs to be on the class each component extends — React.

Component and React.

PureComponent.

That new class, with global state functionality, would extend the original React.

Component or React.

PureComponent.

There are a few different ways to go about this.

I opted for what I consider to be the easiest for any developer: a single byte change.

The package, named reactn, exports an exact copy of React, except the Component and PureComponent properties extend the originals by adding the global member variable and setGlobal method.

Whenever you add this single byte to a file, all references to React.

Component and React.

PureComponent now have global functionality built in, and all references to other React functionality, such as React.

createElement, are completely unaltered.

This is accomplished by copying the references to the same React package you are already using and then to a new object.

reactn is lightweight as a result, as opposed to a copy-paste clone of the React package.

And it doesn’t modify the original React object at all.

But what if you don’t want the React object you’re importing to have these new properties?.I completely understand.

The default import of reactn also acts as a decorator.

No decorator support in your create-react-app?.Class decorators are easy to implement in vanilla ES6.

One of these three solutions should meet the style guidelines of your team.

All three options have no more than one line of “boilerplate” to implement.

But what about setting up the store?.The aforementioned initializer?.The aforementioned redux nightmare?.My best solution thus far is to simply pass a state object synchronously, but I feel it’s an area that could use some improvement from community feedback.

React Hooks“I’m sorry, is this October 24th, 2018?.React Hooks are here now, and I never have to use a class component again!”You’re right.

React global state management solutions should harness the power of React Hooks — after all, functional components use useState.

So in order to be intuitive to what React developers already know and use, there should be an analogous global state hook.

We can offer a completely analogous solution.

As it should, it shares global state with the global text property used in the class component demo.

There’s no reason why functional and class components can’t share their global states.

Using hooks-within-hooks, we can force a component to re-render when a global state property is “hooked” to changes — just as you would expect with local state.

A little more versatile, we can use useGlobal the same way class components use it.

This may be more accessible to the user migrating from classes.

setGlobal also accepts a function parameter, the same way this.

setState does.

Reducers: Modern Staples of State ManagementWith Redux’s dependency on reducers and React 16.

7’s introduction of useReducer, I simply couldn’t pretend that reducers aren’t a modern-day implementation of state management.

How do you manage a third party global state without the boilerplate of reducers?I’ve implemented two solutions.

One, for class syntax:This introduces the familiarity of Redux reducers with less boilerplate.

The functions are smaller and easier to code split, and there are no higher-order components to cloud the React component tree.

Altogether this feels more maintainable to me.

The second solution is inspired by the functional useReducer.

Since useGlobal takes a string property name as a parameter in the aforementioned examples, it is unambiguous that a function parameter should be a reducer.

Like useReducer, you can use this returned reducer to modify the global state.

Your reducers can thus be code split or even imported if that is preferred to the aforementioned addReducer.

If addReducer is preferred, you can still access your added reducers in functional components via const addCard = useGlobal('addCard');.

ConclusionThis isn’t the documentation for reactn, so I won’t detail the bells and whistles.

I do want to outline a system that I believe is significantly more intuitive to React developers, in the hopes that it may inspire creativity that is targeted to React solutions.

There is absolutely no reason a global state package should require so much boilerplate or add so much complexity to a project.

All of the above takes up a whopping 8kB uncompressed.

That’s on par with redux itself, without the need for middlewares to handle asynchronous state change.

If you want to contribute to this project, it is open-source on GitHub, and I would be absolutely ecstatic for more community feedback.

If you want to play around with this project, simply npm install reactn –save or yarn add reactn.

.

. More details

Leave a Reply