Redux vs. Storeon: An App to App Comparison
Redux has been the go to state management library for React apps for a long time. It’s reliable, scalable, and resources are everywhere.
However, it is no secret that Redux setups tend to be quite bloated. Writing a new action + action creator + reducer trio can require a hefty amount of code.
Even if you are organizing your actions and reducers properly, a fair amount of file jumping tends to be the norm.
Storeon: Redux’s Sexier Cousin
If you have been struggling with Redux but you haven’t checkout out Storeon, please read this article.
Storeon is the slimmer, faster, and straightforward cousin of Redux. To summarize the benefits:
- It’s only 173 bytes.
- Async actions are built in.
- No selector functions to deal with. It only re-renders components subscribed to changed state.
- No separation of “reducers” and “actions”. You can keep everything in one place while still maintaining readability.
The Benchmark React App
Our Basic Counter App
I built the same React app twice: one backed by Redux, and one backed by Storeon. The app is a simple counter. Each update of the counter hits a fake api (setTimeout for 1 second), sets the loading state of the buttons, updates the counter, then logs the decrement or increment in an “action history” list.
Here’s the link to the Redux project
Here’s the link to the Storeon project
Folder Structure
I understand this varies from project to project, so your experience may differ. With Redux, it’s not a bad idea to organize your actions and reducers into folders.
We have a reducer responsible for keeping count, count.js
, and another responsible for logging to the history, history.js
. Then we combine these reducers in our index.js
file.
Storeon, as you will see why later, doesn’t really need a separation of “actions” and “reducers”. With Storeon, you break up your data into isolated modules, and because of the way they are written, you can have a nice clean folder structure.
Here’s a comparison of Storeon’s folder structure vs Redux:
Storeon vs. Redux (folder structure)
With Storeon, one module is responsible for the counting, and the other for history. The folder structure is simple, and self explanatory. Even if you never worked with Storeon before, you can figure things out pretty quickly. Redux, on the otherhand, requires context on what constitutes an action and a reducer before they can understand the folder structure.
Creating a Store
With Redux, creating a store with all the things we want in it is a little verbose. With Storeon, things are a lot simpler. Let’s see them side by side:
store.js (left: Redux, right: Storeon)
In Redux, we need composeWithDevTools
and composeEnhancers
if we want to work with redux dev extension. The applyMiddleware
function is needed if we want thunk, loggers, analytics middleware, etc.
We import our “root” reducer from reducers/index.js
which calls combineReducers
in order to construct this single root reducer. Here’s what it looks like:
reducers/index.js
Because Storeon deals with modules, everything you feed to createStore
, including middleware, is just an array of modules!
Also, Redux dev tools works out of the box with Storeon. Just install the redux devtools extension and add the devtools middleware like any other module.
One more benefit with Storeon is that if your list of modules starts to get long, you can easily automate this with the following example (assuming you are using Webpack):
easy module resolver (assumes modules live in same folder as this function)
As long as you follow the naming convention [name].module.js
, you never have to worry about connecting your modules to your store again!
A nice bonus is that we can use redux dev tools extension with Storeon out of the box. Our store is set and ready to be used by the store provider.
Store Provider
There’s no significant difference here.
Redux on the left, Storeon on the right
Actions & Reducers (Redux)
As we saw at the folder level earlier, with Redux we split up our reducers into separate files, then combined them in an index.js
file under /reducers
, and exported the result to be used by our store.
Let’s take a closer look at how our reducer for counting is setup in Redux:
count.js (Redux reducer for our counting state)
To give you some context, all we basically have are a few actions to increment and decrement the counter, as well as an action to let the UI know when the counter is “loading”.
What does the reducer for Storeon look like?
Well… if I show you, it will spoil everything, so bear with me while I show you the Redux action creators for updating the counter:
async action creators (Redux)
If you are familiar with Thunk middleware, this is nothing special.
- We dispatch our “loading” actions called
INCREASING
andDECREASING
. - Then we call our fake api (passing in # of seconds to wait), and dispatch our
INCREMENT
orDECREMENT
actions when the promise resolves. - Then we call an action called
LOG_HISTORY
, which we’ll take a look at soon.
If you are use to Redux, this may seem like a simple enough process.
Events & Modules (Storeon)
Storeon will be new for many of you, so I’m going to break it down into bite sized pieces. We will start by creating a module to handle our counter state.
basic module and state initialization
When working with Storeon, you work with events and modules. Modules are how we organize different pieces of state logic, and the events we setup inside of those modules define how we interact with that state from the outside world.
- We begin by creating and exporting our module called
countModule
. Storeon will pass the store object to each of the modules we pass tocreateStore
, as I showed earlier. - The store object is how we setup events, and dispatch actions. Storeon has a few built in events, such as
@init
, which is where we setup our initial state. See the docs to read about the other built in events.
Okay, so there isn’t enough here yet to make a good comparison between Redux and Storeon. Let’s add a couple of events that will act as “reducers” in Redux terms:
Our first action events!
Right below our initialization event, we create two new events named count/increasing
and count/increase
. Basically, this is like our “loading vs. loaded” actions. We use a path naming convention [module-name]/[event-name]
to avoid name clashing, since storeon references events by a unique string (like Redux reducers).
You might have noticed that there aren’t any “reducers” and “actions” in the exact same sense as Redux. Well, with Storeon, there isn’t a strict separation of the two in the way we use them in Redux.
In our UI, if we called store.dispatch('count/increase')
, our event would be triggered, and the state would be updated.
This is great, because now we have both our “action” and “reducer” in one place, making it much easier to follow!
Events vs. Action Creators
Let’s make an event that calls these “reducer” like events we just created, then calls an api, and finally updates our counter. Here’s a screenshot with our Storeon event on the left, and our Redux action creator equivalent to the right:
Async actions: Storeon vs. Redux
Which one looks easier to follow to you? With Storeon, async events are built in. No middleware required. Because our event is defined within our module, we already have store
in scope.
You may have also noticed that we don’t have to return state with Storeon. We can dispatch other events that are responsible for updating state. Again, all of this comes out of the box.
In the line store.dispatch('history/log', true)
, we pass a “payload” value to let the history logger (we will take a look at that in a moment) know that we increased a value rather than decreased. We could have passed a number, a string, or an object if we wanted to.
Keep in mind that the Redux action is defined completely separate from the reducer. Our action lives in actions/index.js
while our reducer lives in reducers/count.js
. This isn’t necessarily a requirement by Redux, but the structure feels very encouraged. On the flip side, with Storeon we can put all of our logic in count.js
and still keep everything readable.
Modules vs. Reducers
Our count.js
file is a bit long, so I’ll show you a side by side of the history.js
file so you can get a better comparison between a Storeon module and a Redux reducer at a glance:
history.js (left: Storeon, right: Redux)
Now, it’s not 100% accurate to compare these two files directly. Even though we’re just updating state in our history/log
event, much like our LOG_HISTORY
action in Redux, we aren’t limited to that. We could easily add an async event that triggers other events right below our history/log
event:
Storeon async event (note: this is not apart of the benchmark app)
With Redux, we don’t do that. Our reducer is our reducer, and that’s it. So the second we need some async functionality and multiple actions triggered, we have to create a thunk somewhere else.
Selecting State & Dispatching Actions
Luckily, React Redux introduced hooks so actually getting the state and dispatching actions really isn’t an issue. Here is a side by side of Storeon and Redux:
Storeon vs. Redux
With Storeon on the left, we just need to specify the names of the variables we are pulling from state. This lets Storeon know what part of state the component needs to listen to for updates, while also preventing unnecessary re-renders. We can also pull out the dispatch function directly from useStoreon
.
With redux on the right, we make a separate useDispatch
call to get the dispatch function. We also need a selector function to determine what slice of state to return. This requires some careful handling to make sure we aren’t subscribing to state we don’t need. We can also pass a comparison function, in this case, shallowEqual
.
The button to increment our counter
Pretty similar here. A benefit of Storeon is that we don’t have to import anything like in the Redux version. We dispatch it by it’s event name, pass any values we need to, and that’s it.
That being said, you may want to at least export event names from your module so you don’t have to run around updating references every time you change the name.
Resources & Documentation
There’s no comparison. All of your Redux questions have probably already been asked and answered. Documentation is abundant. Storeon, on the other hand, doesn’t have much more than it’s Github docs, a few Github issues, and a couple articles (not including this one).
TypeScript
Storeon comes with TypeScript declarations! Due to the simplicity of Storeon, creating types for your modules & state is much less verbose than a typical Redux application written in TS.
Which is Better?
You can tell that I lean heavily towards Storeon. It’s lightweight, it’s easy to get started with, it’s very readable and easy to organize, and it solves a lot of the pains that linger with Redux in general.
That being said, I realize that I compared the two with a toy application.
For large scale applications that need fine grain performance tuning, a well backed library, and an abundance of learning resources, extensions, and developers, Redux is obviously the winner.
Until Storeon is tested on a large application, it’s hard to tell exactly how limited (or powerful) it is in comparison to Redux.
However, if you’re tired of the headaches that come with Redux while building your small to medium sized application, Storeon may be the gold mine you’ve looking for.
So here’s the verdict
It depends on your project! If you have a small or medium sized web app, give Storeon a try. You won’t regret it. If you are working on a large project, Redux is probably the safer bet for now.
Final Words
As I get more familiar with Storeon, I will be writing more about it in the future. I use both state management libraries on current projects, and so far I haven’t found a reason not to switch to Storeon.
If you like articles like these, please share with your developer friends! You can also find me on instagram.