Gilad lev-ari
Apr 2 · 5 min read

After playing around with React Hooks, I have to say it is just great. Many of the heavy lifting things you had to do when using React class components, are now much easier to implement using a built in hook or one of the many custom hooks you can find online. You can of course write your own hooks. Just like I’m gonna show next.

If you are not familiar with React Hooks, you may want to take a look at my previous post on getting started with Hooks.

One of the things I’ve noticed missing, is an official Redux implementation using hooks. You can find examples global state manager online. But I was just unsatisfied with what I found. Beside, I wanted to learn how to do it on my own.

If all you want is the code, you can take a look at my git “use-hooks-store” repository. An implementation can be found in the “todoitall” repository. Where I create four very simple and minimized “todo” apps using React setState, Redux, useState and my own “use-hooks-store” custom hook.

If you want to read more about the process, stick we me and continue reading.

I’ve written it all in a single file to make it short and simple. The entire file is under 160 lines of code, including validations, prop-types and a nice long debug message 😆.

Let’s start with the basics.

Creating our store:

Now, let’s explain the code:

We create a Context using React’ context API. This will enable us to pass down properties from our top component (the state provider), down to all relevant child components without polluting non relevant components on the way

Then, we create our Context.Provider passing it our new store as the context value. The store is the equivalent to the Redux store, and it keeps our global state. we’ve created this store using the useStoreCreator custom hook. We’d like that our store will act as close as possible to Redux store, this is why we use the built in useReducer hook.
useReducer gets two parameters, a reducer and the initial state. We call it by passing it our reducer function and the same reducer dispatched with the initial state, to set a default state.

One last thing we do, is we attach to the store some “private” properties. This is because we use the context API. When using a context provider the context value mutation functions are part of the context itself. Don’t worry, we will clean up a little before exporting it to the user.

Creating the hook:

First things first. Clean our store from all those not wanted “private” props. This is easily done in getClearState . We create a copy of our store and from this copy, we delete all the “private” properties. Next we take our clean state and return it to the user.

That is it. WAIT WHAT?! 😖
That could be it, BUT, I wanted more. So let’s see how we can make more of our useStore hook.

Adding A simple debug option:

The first thing I wanted to add to my store, was the ability to track my actions and state mutation.

Using the useEffect hook, we can be notified when ever our store has changed. Our store will only change, when call our useReducer hook (attaching new properties to the store, will not dispatch an effect).

Exporting a combineRecuder function:

The next thing I wanted to add, was the ability to combine reducers. Not every app, is as simple as my “todo” app.

I’ve commented out the first line, but we will get back to it later on.
A part from this line, the code is not that difficult to understand.
The combineReducer function, receive an object reducers . This object, is a dictionary of “string”, “function”. We return a function which receives, the current state and an action just like a regular reducer. Then, for each key in the dictionary, we will call the partial reducer with the same state and action.
This will result one state combined from all reducers.

One more small step to … US!

NOT enough 😠
We want it to be more like our beloved Redux.
We want “mapStateToProps”,
We want “mapDispatchToProps”,
We want “devTools”
And we want Async Actions!

OK, so let’s go on leaving the sad faces behind and just code. We’re gonna add all those things to our store, step be step.

Enabling a “mapStateToProps” like option:

Ok I kept it simple (we’ll add the validations in a few sections ahead).
Our useStore gets a single param mapStateAs and if exists, assuming it is a function, we call it to get our mapped state.

Enabling a “mapDispatchToProps” like option:

Ok! That’s a lot of code, let’s go over it. First, instead of just receiving amapStateAs parameter, we also get a mapActionsAs parameter. We spred them out as maps . The state mapping, keeps the same as before (for now).
If we do have a mapActionsAs parameter, we treat it as an object (just like with mapStateAs, we’ll add the validations in a few sections ahead). We go over the object’s keys, and reduce them into an object of actions. Every prop in this object, is a function. This function returns the dispatcher function result.
The dispatcher function receives the actionCreator from themapActionsAs object. If the actionCreator returns a promise, the dispatcher awaits it to resolve before dispatching its result else is just dispatch it.

Connecting our debugger with the Redux devTools:

Now I was thinking, ‘what if, I could improve my log options?’
💕 Redux devTools! 💕

We are using the same useEffect hook as before. Now instead of just using one useEffect we are using two. The first effect hook, will only run once on the first render. We do so by passing an empty array as the second parameter after the function. In here, we check if Redux devtools exists on the window object and if so, we connect / initialize it and setting a flag saying devTools is on.

The second effect hook is quite the same as before, only it checks to see if the devTools flag is on and if so, it sends the action + state to it instead of console logging it.

Adding input validations:

I don’t want to make this post any longer than it already is. So what I’m gonna do, is tell you about this nice npm package called superstruct which enable you to validate your parameters just like prop-types does for components.

This is the validation for the useStore hook parameters.

const validateUseStoreStruct = struct({
mapStateAs: 'function?',
mapActionsAs: 'object?'

Then we just call the function validateUseStoreStruct any time we want. This struct above, checks if we passed it an object with two properties mapStateAs and mapActionsAs each can be null (see the ‘?’) BUT if you do pass a parameter each has to be of the type you asked for. You can create more robust and complicated structs if you need to.

Not it is really it. 😃 It was a little long and full of emojis but I hope you liked this post and found it useful.

Gilad lev-ari

Written by

Full stack web engeneer with over a decate of experience and true passion for front end development and JavaScript 💪

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade