DEV Community

Vanderlei Alves da Silva
Vanderlei Alves da Silva

Posted on

Middlewares with react context and hooks

Continuing the idea explored in the previous article of having a global state management using pure react (with react context and hooks), we’re going to explore now how to take advantage of the middlewares concept, implementing for that a loger and localStorage middleware to our todo app, check here the live demo and here the source code

About middlewares

The term can slightly differ from each depending on the middleware type (Database Middleware, Integration Middleware, Application Middleware, Object Middleware, Remote Procedure Call (RPC) Middleware, Message Oriented Middleware ...) but essentially they have the idea of a composable peace of code running in the middle of to distincts processes improving their communication, and by process we could use more specific terms according to the scenario that we are talking about.

In the web development niche this term is spreedly used in server side technologies such as Laravel, ExpressJS, nestJS among others as:

A composition of functions that are called, in a given order, before the route handler and that usually each one of them have access to the request information and a reference to the next function in this chain

This idea was taken by the front-end fellows, mainly applied by the state management libraries: redux, mobx, vuex (the last even though with a different nomenclature “plugin” the idea is the same), and what they all do is to provide a way of running some code between dispatching an action, and the moment it changes the application state.

Of course this concept can be used in other scenarios, this article explores its usage attached to the router change in angular, getting closer to the above mentioned server-side ones. But for now we’re going to explore the first one.

Show me the code

import { initial, final } from './log';
import localStorage from './localStorage';

export default ({ state, action, handler }) => {
  const chain = [initial, handler, localStorage, final];

  return chain.reduce((st, fn) => fn(st, action), state);
};

That’s all what matters, we need a function to create a middleware chain and execute all them in a given order and of course call our handler (the reducer function called by a given action in our application).

const chain = [initial, handler, localStorage, final]

Here we define the middlewares that will be called and in which order they will, the ones that comes before handler are the pre-middlewares (you put here all middlewares that you want to run something before the state have changed) and the others the post-middlewares (the ones that execute something with the new state).

The middleware function signature follows the same pattern of the reducers:

(state, action) => newState

As an example here is the initial log middlewares:

const initial = (state, action) => {
  console.log(action);
  console.log(state);
  return state;
};

The middleware just log the initial state (before the state have being change by the reducer) on the console.

Here we have a more interesting one:

import store from 'store2';

export default state => {
  store.set(state.storeId, state);
  return state;
};

This middleware saves the current state on the local storage, I’m using for this a small library store2 just to make sure retrocompatibility with old browsers and also avoiding working with try catch statements.

I have on the app state an storeId property with the name of the key that will be saved on the local storage, so basically in this call:

store.set(state.storeId, state);

I store in the given key the given state. If you check the app again, play around and refresh the page, the information will still be there.

And lastly we have:

return chain.reduce((st, fn) => fn(st, action), state);

We use the reduce array method to iterate over each item of the chain getting the result of the previous and passing to the next item.

There it is

We have now returned to the basics and explored how the main state management libraries conceptually work with middlewares, giving us the same results with less dependencies and less complexity. We now understand what happens, instead of just blindly using them.

What do we got from it!? A better reasoning of when to use these state libraries.

What do we go from now!? More hooks on the way, check here the new custom hooks from react-router-v5.1 and see you soon. ;)

References

https://dev.to/vanderleisilva/global-state-management-with-react-hooks-and-context-5f6h
https://vanderleisilva.github.io/react-context/
https://laravel.com/docs/6.x/middleware
http://expressjs.com/en/guide/using-middleware.html
https://docs.nestjs.com/middleware
https://redux.js.org/advanced/middleware
https://github.com/mobxjs/mobx-state-tree/blob/master/docs/middleware.md
https://vuex.vuejs.org/guide/plugins.html
https://www.w3schools.com/jsref/jsref_reduce.asp
https://reacttraining.com/blog/react-router-v5-1/

Discussion (0)