I’ve been working with React for quite some time now. Anybody who’s done that, has faced the issue of managing global state in their app and given its popularity, probably ended up using Redux.
When I encountered Redux for the first time, I ran away in horror. The sheer number of new concepts that I had to learn, was just overwhelming. I just wanted to share some state around 😕. I tried out a bunch of other solutions
MobX
Unstated
Context API
They’re all great (relatively speaking). However Redux still reigns supreme somehow. I couldn’t just keep avoiding it. So I eventually decided that something had to be done. I decided to learn Redux by making it my own. No, I wasn’t planning on forking the Redux repository and making contributions to it. I don’t have anywhere near the amount of grey matter required to do that. I decided to learn Redux by making my own measly state management library. Following were my aims
Learn Redux (The good and bad parts)
Recreate it without the bad parts, while still keeping the good parts around
The Bad Parts
Redux is overly verbose. It fragments the hell out of your codebase. Separation of concerns is one thing but Redux shreds your apps to bits. There is no cohesion. Its hell to understand the flow in a Redux app. To be able to make an asynchronous update to your state (for e.g. as a result of an API call), you need to resort to things like Thunks, Sagas, Epics. Have to create separate Actions (and Action Creators) and Reducers for handling the in progress, success and failure states spread across multiple files. Furthermore, It encourages you to have a single global store. You can create separate reducers, but conceptually a reducer shouldn’t be responsible for this separation. To summarize, following is what I despise in Redux
Too much to learn
Code fragmentation
No built in way to handle asynchronous flow
Single global store
The Good Parts
Redux is (extremely) popular and prevalent. You just can’t avoid it. Besides this, the only other redeeming quality I could find was its architecture. Its based on the Flux pattern
Flux is a pattern for managing data flow in your application. The most important concept is that data flows in one direction.
Flux makes a lot of sense. It basically ensures that the all state changes in your app go through a strict pipeline, in theory making debugging easier. Read up on it here. It has a ‘few’ core concepts
Actions: These represent the operations that can be applied to the state usually implemented as plain objects describing the ‘type’ of the action and any associated data.
Dispatcher: Its a singleton that routes actions to stores
Store(s): Its where your data lives. In Flux, the Stores are also responsible for responding to actions by changing their internal state.
Views: This is where React usually comes in. React components subscribe to the Stores and listen for change events, so they can be re-rendered.
A Better Flux Implementation
So, basically what I wanted to do at this point was to make a ‘better’ (than Redux, IMHO) implementation of the Flux architecture (and learn Redux in the process 😖). And I did it with react-3ducks.
smakazmi / react-3ducks
Simple state management solution for React
react-3ducks 🦆 🐥 🐣
A simple react global state management solution
Installation
npm install react-3ducks
Why
Redux is the current prevailing solution to manage global state in React apps. However, there are a few shortcomings to it that this project attempts to address. Some of them are as follows
- Encapsulation: Redux by convention has only one global store and it encapsulate only data. The behavior is fragmented between actions, action creators, reducers, epics, sagas, thunks etc. react-3ducks encourages and facilitates creation of multiple stores for various concerns in the application and also allows behavior encapsulation in the stores.
-
Asynchronous Behavior: Redux has no built in way of handling asynchronous actions. react-3ducks fully supports asynchronous behavior through regular
async/await
orPromise
based semantics that are already known and loved. - Complexity: Its hard to quickly grasp what's going on in a Redux app. Mainly…
Its not a joke, It just sounds like one 😄.
What I discovered was that ‘Redux’ took the whole Flux pattern too literally. In my opinion, Redux doesn’t do as much as it could to make our lives easier. Let’s see an example of how I think it could be done. Introducing the StateStore class
StateStore: A class that accepts some initial state in its constructor and provides a method called setState that accepts and applies partial changes to the said state. Sounds familiar so far ? This is exactly how internal state works in React Components. setState also fires events to allow subscribed components to re-render.
StateStore is basically the combined implementation of the Store, Action and Dispatcher concepts. An implementation of StateStore could be as follows
There’s no boilerplate here. Just a class that has some methods, which interact with the state. StateStore takes care of informing any concerned components of the changes. Notice the addRandomTodo method that fetches a random quote via axios and adds it as a todo. Just a plain and simple async javascript method. No juggling required with multiple Actions, Actions Creators, Reducers, Thunks, Sagas, Epics etc.
But we need a mechanism for components to subscribe to the state as well. That’s where the container higher order component comes in
This is similar to the connect helper found in react-redux. The first parameter is obviously the component being wrapped. The second optional parameter is used to map container props to store methods or state. If its not provided, all the store instance(s) are passed as props to the wrapped component. Not to worry though, in either case, it makes clever use of ES6 proxies to only subscribe to the state that’s used in the component’s rendering. Its pretty neat.
We created a store, that we then initialized with some state and exported. The we created a container component that subscribed to the store. We still need to provide the store instances to our app somehow. That’s done through another higher order component called root.
The root higher order component basically makes the stores available to any child container components in the hierarchy.
See this example in action here
That’s the entirety of the API that you need to know to use react-3ducks.
StateStore
container
root
Check it out and let me know your feedback. Contributions to the project are very welcome.
I’ll probably do another post on the internals of the library later.
Top comments (0)