How to introduce redux in an AngularJs Web App
Now there is a wide choice of frameworks to develop a web app, first of all React, Vue.js and the new Angular too. In spite of that, many systems are still developed in AngularJs. Migrating to a new framework it's all but painless, and very often you can find a legacy part in AngularJs, difficult to maintain, debug, or just understand. I have been in this situation, and to solve my struggling I used Redux, an easy to use state container.
Why Redux
AngularJs is characterized by the two-way data binding, sometimes very handy, too many times unpredictable. When the state become complicated or it changes too many times in many different ways, it's easy to loose control on it.
Redux allows you to manage the state at any moment, and to keep in one place all the logic that can change it. Moreover, having the applicative logic separated and independent by the framework used, will save you a lot of time when will be the moment for a migration to a different framework.
How to use Redux
First of all, I installed ng-redux.
Then I added in the file app.module.ts
as config my redux configuration file:
import { combineReducers } from 'redux';
import reduxPromiseMiddleware from 'redux-promise-middleware';
import reduxThunk from 'redux-thunk';
import { reducer1 } from './reducer1.reducers';
import { reducer2 } from './reducer2.reducers';
import _ from 'lodash';
export function reduxConfig($ngReduxProvider){
var rootReducer = combineReducers({
reducer1 : reducer1,
reducer2 : reducer2
})
$ngReduxProvider.createStoreWith(rootReducer, [
reduxThunk,
reduxPromiseMiddleware()
] );
}
reduxConfig.$inject = ['$ngReduxProvider'];
Here I created the reducers tree structure, combined with all the reducers I need among the code. The rootReducer
will maintain the global state.
In this way the reducer tree root is kept in app.module.ts
, while the different reducers are in the relative modules.
In the specific module, in order to use redux, you have to define the actions, that will trigger state modifications, and the reducers, such as pure functions that, given and action and the previous state, create the new state. I prefer to keep all the actions of the same module in a single file, and if necessary split the reducers among the sub-modules. I defined an ActionRequest
object, in this way:
{
type: ActionType, //enum of action triggering the modification
payload: any //what I need to update the state
}
When an action request is invoked, the object ActionRequest
is dispatched and handled by the reducer specific for that action.
Then, in the constructor, I subscribe to redux the controller that needs to read the state of reducer1 and invoke actions in this way:
var unsub = this.$ngRedux
.connect(
(state) => state.reducer1,
_.assign({}, reducer1Actions))
((state, actions) => {
this.actions = actions;
this.model = _.assign({}, state)
});
this.$scope.$on('$destroy', unsub);
So everything is set now, and all you need to do is to invoke an action, and your state will update and will be readable by all the components subscribed to that reducer.