Redux-starter-kit usage

Why?

  • Use redux-starter-kit to general boilerplate code for redux to faster the development for react app.
    setup store is anoying
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import { applyMiddleware, compose, createStore } from 'redux';
    import { composeWithDevTools } from 'redux-devtools-extension';
    import thunkMiddleware from 'redux-thunk'

    import monitorReducersEnhancer from './enhancers/monitorReducers';
    import loggerMiddleware from './middleware/logger';
    import rootReducer from './reducers'

    export default function configureStore(preloadedState) {
    const middlewares = [loggerMiddleware, thunkMiddleware];
    const middlewareEnhancer = applyMiddleware(...middlewares);

    const enhancers = [middlewareEnhancer, monitorReducersEnhancer];
    const composedEnhancers = composeWithDevTools(...enhancers);

    const store = createStore(rootReducer, preloadedState, composedEnhancers);

    if (process.env.NODE_ENV !== 'production' && module.hot) {
    module.hot.accept('./reducers', () => store.replaceReducer(rootReducer));
    }

    return store;
    }
  1. typically you will first create the const action type and action creaters in two different file2.
    For example: you have a container component that has a button that when clicked, dispatches an action thru a bound action creator. To see how Redux handles this action (e.g. state changes via reducer and side effect via middleware), you would typically do the following steps:

Go to the file where the action creator was defined and see what action type constant is being used.
Go to the reducer file where the action type constant you found in the previous step is being used and see how the action is being handled via the switch statement in your reducer.
If you have a middleware like redux-saga, you would go to the saga file and also look for the the action-type constant and see how the action is being handled. Also in this file, you would normally see both the action type constant and action creator being imported.

two seperate files for action type and creaters

the first steps const and creaters is anoying, right?

What if you could skip step one and just use your action creator to handle an action in your reducers and middleware? What if you could reduce the amount of files that are being imported/used and just have one file that represents an action?

Problem Touching 4 files to implement a simple state

Have you added a state in your component using redux and you end up touching constants.js, actions.js, reducer.js and container-component.js? What more if you have a middleware for side-effect like saga?

This process can be tedious for implementing things that are so simple. Also, having too many files just make your code fragmented that it ends up difficult to reason about.

What if you could increase cohesion by bundling these pieces together into an isolated, self-contained module?

What?

Addressing Problems above with Redux Starter Kit
Redux Starter Kit is a library created by the same team who maintains Redux, and it offers a set of tools to make using Redux easier. Now, let’s see how this library can solve the 3 problems we mentioned above.

How?

  • configureStore
    Redux Starter Kit has a configureStore function that abstract a lot of boilerplate code (like we had on Problem #1) and adds some defaults to make setting up a Redux store a breeze

Setup store will be like:

1
2
3
4
5
6
import { configureStore } from 'redux-starter-kit';
import rootReducer from './reducers';

const store = configureStore({ reducer: rootReducer });

export default store;

configureStore also allows you to pass some options so you could customize your store:

1
2
3
4
5
6
7
8
9
10
11
import { configureStore, getDefaultMiddleware } from 'redux-starter-kit';
import monitorReducersEnhancer from './enhancers/monitorReducers';
import loggerMiddleware from './middleware/logger';
import rootReducer from './reducers';

const store = configureStore({
reducer: rootReducer,
middleware: [loggerMiddleware, …getDefaultMiddleware()],
preloadedState,
enhancers: [monitorReducersEnhancer],
});

  • createAction

This library has a createAction function that combines action type and action creator declarations into one, so you don’t have to create 2 separate files (constant.js and some-action.js).

This function returns an action creator that exposes a .type property so you can use it in your reducer and middleware to handle dispatched action. Here is an example of have you would create an action creator using createAction and how you can use the .type property in a reducer and saga:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// todo-actions.js
import { createAction } from 'redux-starter-kit';

// Create an action creator
export const addTodo = createAction('TODO/ADD_TODO');


// todo-reducers.js
import { addTodo } from './todo-actions';

export function todosReducer(state = [], action) {
switch(action.type) {
// Use the action creator's type property to handle action
case addTodo.type:
return state.concat(action.payload);
default:
return state;
}
}


// todo-sagas.js
import { addTodo } from './todo-actions';

function* addTodoSaga({ payload }) {
// Some side effects
}

// Use the action creator's type property to handle action
export default takeLatest(addTodo.type, addTodoSaga);
By getting rid of the constant file for your action type, we reduce the amount of mappings we have to make between your React component and your Redux files when debugging. Plus, less files to import!

* Solution to Problem #3 — createSlice

Redux Starter Kit has a createSlice that allows you to put together pieces in Redux that are logically related to each other into a module.

Think of it as putting together pieces that works on a slice of state in the Redux state tree.

Example:

import { createSlice } from ‘redux-starter-kit’;

const todoSlice = createSlice({
slice: ‘todos’,
initialState: [],
reducers: {
addTodo(state, action) => [ …state, action.payload],
removeTodo(state, action) => state.filter(todo => todo !== action.payload),
},
});

// Extract the action creators object and the reducer
export const { actions, reducer } = todoSlice;

// Export the reducer, either as a default or named export
export default reducer;
`

Isn’t that better than creating 4 files for just a single state?

To know more about the concept behind createSlice, take a look at the “Redux Ducks” pattern.
https://github.com/erikras/ducks-modular-redux