Redux combineReducers example

From Redux Introduction, we know that there is only a single Store in a Redux application. When we want to split data-handling logic, we will combine Reducers instead of using many Stores. This tutorial shows you way to use Redux combineReducers() function in a Redux example.

Full example with filtering and sorting: Redux Reducer example – filter & sort data

Overview

combineReducers

If we have state whose values seem to correspond to different reducing functions like this:


const demoState = {
    books: [
        {
            id: '123abcdefghiklmn',
            title: 'Origin',
            description: 'Origin thrusts Robert Langdon into ...',
            author: 'Dan Brown',
            published: 2017
        },
        {
            ...
        }
    ],
    filters: {
        text: 'ori',
        sortBy: 'published',
        startYear: undefined,
        endYear: undefined
    }
};

We won’t use only one Reducer to handle Action logic but two Reducers: one for books, and one for filters. Now, combineReducers() comes to be the solution.
It combines 2 reducing functions into a single reducing function that can be passed to createStore():


const rootReducer = combineReducers({
    books: booksReducer,
    filters: filtersReducer
})

const store = createStore(rootReducer);

combineReducers rules

The Reducer that is passed to combineReducers() function must satisfy these rules:
– For unrecognized Action: return the given state as the first argument.
(we usually return state in default case of switch statement)
– Never return undefined.
– If the given state is undefined: return the initial state. So the initial state must not be undefined either.

Example Description

We will create a Redux Application that has:
– state that contains 2 components: books array and filters.
– 3 types of Actions:
+ 'ADD_BOOK', 'REMOVE_BOOK' for books.
+ 'FILTER_TEXT' for filters.
– 2 child Reducers (booksReducer and filtersReducer) that will be combined using combineReducers() function.

We can add/remove books to/from books, set filters.text value.

Practice

Setup environment

In package.json:


{
  ...
  "dependencies": {
    ...
    "babel-plugin-transform-object-rest-spread": "6.26.0",
    "redux": "3.7.2",
    "uuid": "3.2.1"
  }
}

Then run cmd yarn install.

Add plugin to .babelrc:


{
    ...
    "plugins": [
        "transform-object-rest-spread"
    ]
}

Create Redux Actions

Book Actions


const addBook = ({
    title = '',
    description = '',
    author = '',
    published = 0
} = {}) => ({
    type: 'ADD_BOOK',
    book: {
        id: uuid(),
        title,
        description,
        author,
        published
    }
});

const removeBook = ({ id } = {}) => ({
    type: 'REMOVE_BOOK',
    id
});

Filter Actions


const filterText = (text = '') => ({
    type: 'FILTER_TEXT',
    text
});

Create Redux Reducer

Books Reducer


const booksReducerDefaultState = [];

const booksReducer = (state = booksReducerDefaultState, action) => {
    switch (action.type) {
        case 'ADD_BOOK':
            return [
                ...state,
                action.book
            ];
        case 'REMOVE_BOOK':
            return state.filter(({ id }) => id !== action.id);
        default:
            return state;
    }
};

Filters Reducer


const filtersReducerDefaultState = {
    text: '',
    sortBy: 'title',
    startYear: undefined,
    endYear: undefined
};

const filtersReducer = (state = filtersReducerDefaultState, action) => {
    switch (action.type) {
        case 'FILTER_TEXT':
            return {
                ...state,
                text: action.text
            };
        default:
            return state;
    }
}

Create Redux Store


const store = createStore(
    combineReducers({
        books: booksReducer,
        filters: filtersReducer
    })
);

Subscribe & Unsubscribe


const unsubscribe = store.subscribe(() => {
    console.log(store.getState());
});

// store.dispatch(action)...

unsubscribe();
// store.dispatch(action) << not do anything...

Run and Check result

Full code:

import { createStore, combineReducers } from "redux";
import uuid from 'uuid';

const demoState = {
    books: [
        {
            id: '123abcdefghiklmn',
            title: 'Origin',
            description: 'Origin thrusts Robert Langdon into the dangerous intersection of humankind’s two most enduring questions.',
            author: 'Dan Brown',
            published: 2017
        }
    ],
    filters: {
        text: 'ori',
        sortBy: 'published', // published or title
        startYear: undefined,
        endYear: undefined
    }
};

// action creators for Books Reducer
const addBook = ({
    title = '',
    description = '',
    author = '',
    published = 0
} = {}) => ({
    type: 'ADD_BOOK',
    book: {
        id: uuid(),
        title,
        description,
        author,
        published
    }
});

const removeBook = ({ id } = {}) => ({
    type: 'REMOVE_BOOK',
    id
});

const booksReducerDefaultState = [];
// Books Reducer
const booksReducer = (state = booksReducerDefaultState, action) => {
    switch (action.type) {
        case 'ADD_BOOK':
            return [
                ...state,
                action.book
            ];
        case 'REMOVE_BOOK':
            return state.filter(({ id }) => id !== action.id);
        default:
            return state;
    }
};

// action creators for Filters Reducer
const filterText = (text = '') => ({
    type: 'FILTER_TEXT',
    text
});

const filtersReducerDefaultState = {
    text: '',
    sortBy: 'title',
    startYear: undefined,
    endYear: undefined
};
// Filters Reducer
const filtersReducer = (state = filtersReducerDefaultState, action) => {
    switch (action.type) {
        case 'FILTER_TEXT':
            return {
                ...state,
                text: action.text
            };
        default:
            return state;
    }
}

// Store
const store = createStore(
    combineReducers({
        books: booksReducer,
        filters: filtersReducer
    })
);

const unsubscribe = store.subscribe(() => {
    console.log(store.getState());
});

const book1 = store.dispatch(addBook({
    title: 'Origin',
    description: 'Origin thrusts Robert Langdon into the dangerous intersection of humankind’s two most enduring questions.',
    author: 'Dan Brown',
    published: 2017
}));

const book2 = store.dispatch(addBook({
    title: 'Harry Potter and the Deathly Hallows',
    description: 'The seventh and final novel of the Harry Potter series.',
    author: 'J. K. Rowling',
    published: 2007
}));

const book3 = store.dispatch(addBook({
    title: 'The 100-Year-Old Man Who Climbed Out the Window and Disappeared',
    author: 'Jonas Jonasson',
    published: 2009
}));

store.dispatch(removeBook({ id: book2.book.id }));

store.dispatch(filterText('origin'));

Result in Browser Console:

redux-combineReducers-example

Source code

ReduxCombineReducers

For running:
- yarn install
- yarn run dev-server or yarn run build, then yarn run serve.



By grokonez | April 13, 2018.

Last updated on May 20, 2021.



Related Posts


2 thoughts on “Redux combineReducers example”

  1. Great article, really descriptive and illustrative. Thank you for that.

    In the CombineReducer rules, the last rule states “If the given state is undefined: return the initial state. So the initial state must not be undefined either.”. However alle the examples in redux doc and even in this article implements something slightly different, namely “If the given state is undefined: behave as if the initial state was provided.” which is implemented using default argument on state. I suggest you correct this “rule”.

Got Something To Say:

Your email address will not be published. Required fields are marked *

*