Redhooks: A predictable state container for React applications
A reimplementation of the Redux API using React Hooks.
INTRODUCTION
I have been using React for years, writing either big or small applications. For each one of them, the discussion beforehand was always: should we or should we not use Redux for holding the application state?
Holding app state with a predictable state container based on Redux’s principles like:
- Holding app’s state in a single store.
- Keeping store’s state read-only.
- Transforming the state tree writing pure reducer functions.
It may offer advantages but also adds constraints to your applications.
Anytime we pick a “solution” we are binding our application to the advantages but also to the constraints that the chosen “solution” is based on.
Going back in time before redux or whatever is out there came out, we could write applications without applying those principles. In fact, none of them are mandatory when building an application.
Code Thinking
Thinking back I am reminded of a friend of mine’s quote:
“Design Thinking is something that should be applied for coding too.” — Francesco Pini
After years of coding experience, today I couldn’t agree more with this quote.
“Designing” your application based on your needs instead of current trends makes you a better thinker who is going to work less by working smarter.
As I like to say:
“Code does not come from coder’s hands but from thinker’s minds.“
I would suggest then:
“be a thinker first then a coder.”
Awareness of this makes your day-to-day coding much easier.
I am not pretending to be a philosopher, only a developer. So let’s go back to the main topic of this article and take a look at how to use Redhooks:
WHAT IS REDHOOKS ?
Redhooks is a tiny library for holding a predictable state container within your React applications based on the three principles described in the section above. Inspired by Reduxjs, it reimplements Redux’s API using the experimental Hooks and the Context API.
Before starting to code let’s take a look at the application data flow.
APPLICATION DATA FLOW
The Counter and Greeter components are connected to the store which means that they can dispatch actions into it. Any dispatched action follows a unidirectional flow and gets into the “root” reducer function which, in our application, it is composed by the combination of two reducers. Receiving the previous state and the action as input, the “root” reducer function produces as output the new state.
Now that we have an idea of how it works we can start.
GETTING STARTED
Let’s create our first reducer functions.
Reducer
A reducer is a pure function that takes the previous state and an action which is a plain object and returns the next state.
./reducers.js
As your application grows you may want to divide your state into independent parts, each one of them managed by a dedicated reducer function. In the example above we have two reducer functions: counter
and greeting
. After combining them into one function using the combineReducers
, counter
and greeting
will be able to manage independent parts of the state.
By doing so we will produce the initial following state object:
{ counter: 0, greeting: "good morning" }
Each time an action will be triggered the rootReducer function will be called with the previous state and the action triggered. Then every reducer function you combined using combineReducers
will be invoked with that action deciding whether or not to change its “piece” of the state.
Store
Now that we combined our reducers we are ready to create our store. To do so, we use the createStore
API which takes the reducer function and as second argument, optionally, a plain object in which we can add some extra props like a preloadedState
, initialAction
and middlewares
.
middlewares
, like in redux, are a way to extend redhooks with custom functionality. For more details I invite you to read the documentation.
./store.js
When creating a store
in addition to a preloaded state, we can also trigger an initial action. In the above example, we initialised our counter value to 1 and immediately trigger an “INCREMENT” action. As a result of this now our counter
will have the value 2.
Next, let’s take a look at how we can use the store either for getting the state or for dispatching actions within Class or Function Components.
Function Components
Within function components, in order to access the store, we can use the useStore()
Redhooks API. This returns an object with props that are the state
object and the dispatch
function.
./components/Counter.js
TIPS
state
is read only. Don’t change it by doing things like state = something. To update the state you have todispatch
actions.Don’t use
useStore()
within Class Components because React Hooks are not allowed there. Instead, you can use theconnect
redhooks API.
Class Components
Within a class component due to the fact that React Hooks are not allowed, we need to use the connect
Redhooks API which connects either Class or Function Components to the Redhooks store. It also helps to improve performance by avoiding unnecessary re-renders by checking whether or not the props you subscribed to have changed due to actions.
./components/Greeter.js
Using the mapStateToProps
method we can subscribe any components to the Redhooks store. Any time the store is updated, mapStateToProps
will be called and its results, which must be a plain object, will be merged into your component’s props. In the example above two props are injected, greeting
and dispatch
.
TIP
connect
accepts as it’s second argument either a function or a plain object. You can pass a function mapDispatchToProps that must return a plain object whose values are functions. Or a plain object whose values are action creator functions. If you choose to omit the second argument as we did above by default a prop nameddispatch
will be injected into your connected component.
Now let’s put all together and render our tiny application.
App Component
./components/App.js
Render the application
./index.js
The <Provider />
makes the Redhooks store available to any nested components.
And there you go, we have a live example sandbox for you to play with.
Other Sandbox Examples
Following few open source projects implemented with redux have been migrated to Redhooks:
- Shopping Cart: Sandbox
- TodoMVC: Sandbox
- Tree-View: Sandbox
- Saga-Middleware: Sandbox
- Redux-Thunk: Sandbox
Conclusion
React Hooks API will be released on the 4th of February 2019 and Redhooks depends on them, so it should not be used in production until that day. Instead, if you like you can play with it and contribute even only by giving feedbacks.
I hope you enjoyed reading the article. 👏 Claps are welcome!