Using redux devtools without redux

November 16, 2015 4:39 pm 0 comments

Redux is probably the most popular library to manage the state of React.js applications at the moment. Wasn’t that what Flux is about? That’s it, redux is an alternative Flux library. It’s creator, Dan Abramov, put a lot of effort in make redux a really flexible library, it is possible to extend it, or even change its behavior just adding your own functions to its action life cycle.

Advantages of redux goes beyond the library itself, because redux has an awesome companion called redux-devtools. With the devtools you can see how the app state gets mutated and you can go back and forth in the state history, canceling and repeating actions, what is a really cool feature when developing.

devtools

But I love freezer.js, a different library. It is a such simple and lightweight solution to handle the app state and keep the data changes flowing just in one direction. The problem about using freezer.js is that I don’t have the cool devtools I have with redux… don’t I?

In this article I want to explore redux internals with the purpose of use redux-devtools in a freezer.js application, but you probably could use this info to use redux-devtools with any other Flux library. The result of the investigation is freezer-redux-devtools, a package ready to use freezer and redux-devtools together.

Knowing redux

Before starting, I need to say that I am not a redux expert, maybe some of the terms I use here are not accurate or things can be done much better: Feel free to complain in the comments.

So let’s hack redux-devtools! Hacking is not bad, being hackable is one of the most cool features of redux, you can see it studing how the devtools work.

A redux application stores all of its state in one object and there is also just one function to update the state. That function receives the current state and an action, and returns a new state, the result of applying the action to the current state. The state received should never be modified inside the function, and the state returned will be its replacement for the application.

Never modifying the state directly is how we are getting the benifits of immutability using just functions in redux. It sounds much like the way that freezer works, creating new objects without modifying previous ones.

Redux flux

Every time that a new state is generated the app is re-rendered, hence we have a reactive application.

But, how can you make a so complex function that can handle all the possible changes in an application? The answer is creating it by composition of smaller functions. If your functions receive an state and returns a new one, it is simple to chain multiple smaller functions that handle different state parts in your app. If you want to know more of this basic pattern of redux, you can have a look at its reducer documentation.

redux reducers

The important thing to know for us is that all the changes made to the state go through one only channel (like the event hub in freezer.js), and what’s better: redux makes easy to intercept what’s going through the channel using middleware. Middleware is a special kind of functions that have access to the action and the current state before they get into the big reducer function in order to modify them or to avoid that they generate a new state. Middleware functions can also modify the new state before it gets into the store again.

redux-middleware

 

Creating a middleware we can have access to all the actions that are dispatched in a redux app transparently.

Knowing redux-devtools

Redux-devtools is an independent redux application that lives beside the main one. It has its own store, dispatcher and reducer and it comes with a middleware function that should be applied to the app that we are developing. That middleware allows to update the devtools panel when an action is dispatched by the host app, or dispatch actions in the app when the devtools panel is used.

Let’s see how to add the redux-tools to a redux app:

If we have a look at the app store, we will find the devtools store in it.

The strategy to use redux-devtools in a non-redux application is creating a store like if we were in a redux app, and apply our middleware and the devtools one to it. That way we will make the devtools believe that they are in a redux application. That’s it, your Flux application must dispatch redux actions whenever an action is dispatched, and it also must listen to devTools actions in order to update its own state.

Creating middleware that communicates with redux-devtools

Freezer way of flux is much simpler than redux explanation above. A freezer object is a store that makes easy to update any piece of data in it, creating a new immutable state on every update. Everytime the state is updated, an update event is triggered in the freezer object. The freezer object is also a event hub, it is possible to trigger or listen any event on it, so if we use these events instead of traditional flux actions, we have a complete system to create Flux applications.

Following the strategy mentioned before, to make a freezer app work with redux-devtools we need to dispatch a redux action on any event triggered in the freezer store. Also, we need to reconstruct the freezer state when some action is dispatched in the devTools store. Let’s have a look at how we can achieve this, fist of all, how to include redux-devtools in our freezer app:

In the code, we create a redux store to make redux-devtools believe that it is in a redux application. Then, freezer’s afterAll event is listened to transform all the freezer events in redux actions. Just with this, we will see how every interaction with our freezer app will be reflected in the devtools panel.

To update freezer’s state when we interact with the devtools we have added the FreezerMiddleware function to redux’s middleware stack. It works as follows:

Lot of code! I have tried to comment it well, but there are some things that you must know about the devtools store to fully understand it.

  • PERFORM_ACTION is dispatched by the devtools when a new action has happened in the app, in order to add it to the devtools. We won’t use this kind of actions to trigger freezer events, because they are the product of the freezer events.
  • TOGGLE_ACTION is dispatched by the devtools when an action is activated/deactivated in the devtools panel. We need to reconstruct freezer’s state based on these kind of actions.
  • All the states generated by actions are stored in the devtools and you can find any of them using the id included in the devtool action.
  • When an action is toggled, devtools set its own state to the state previous to that action dispatching. After that, it dispatches all the enabled actions that were in the devtools after the toggled action, so the state is reconstructed as if action was toggled.

There are two main points in this code

  • We replace the devtools dispatcher to get prepared to the changes that will be produced by the devtools.
  • We create a reducer that will modify freezer’s state according to devtool actions and return the updated freezer’s state. That state is the one that will be stored by the devtools as the result of its action.

What I have learned

I made this exercise to get some tools for freezer, but I have learned a lot about redux with it. I can understand now why redux is used by so many devs. It is a really flexible system that can adapt to any project and solves app state management in a very elegant way. I will continue using freezer in my React apps though, both solutions are very similar in concept but Freezer apps need much less boilerplate code and make easier to update big tree states.

I feel that redux-devtools can be used without much hassle by any Flux library that has one dispatcher or entry point for state change, since the communication can be created point to point between both dispatchers. In Flux implementations with multiple stores and dispatchers the things get more complicated, but I think it would be possible to create a mechanism to know what store to update on a devtool change.

If you are thinking to create a middleware to make your flux library work along with redux-devtools these are some nice links for you:

javi

Let’s push your website a bit further

My name is Javier Márquez and I have more than 10 years of web programming and web designing experience. If you have a difficult development to complete, maybe you can stop by and see what I can do for you. You can find me on and Twitter.

Leave a reply


contact meAnything related to web development like javascript and CSS to create responsive designs, or PHP and node.js to make your website work properly, is my pleasure. If you have an interesting project in mind, I can help to make it real.