How to connect React with Redux – react-redux example

We have created a React Application with React Router v4, then we also learned how to use Redux to manage state in Redux combineReducers example and Redux Reducer example – filter & sort data. In this tutorial, we’re gonna combine all of them by connecting React with Redux using react-redux.

Related Posts:
How to filter list with input text – react-redux example
How to sort list – react-redux example
Add Item Form – react-redux example
How to test React Redux application with Jest

More Practice:
React Redux – Firebase CRUD Operations example
React Redux Firebase Authentication – Google Account Sign in/Sign out example

React Redux

connect() function

react-redux is a Redux binding for React that allows us connect React with Redux in an efficient way.
The most important function is connect() that:
– connects a React Component with a Redux Store.
– returns a new connected Component class without modifying the Component class passed to it.

This is how we use connect() function:

...
import { connect } from 'react-redux';

const BookList = (props) => (
    
Use {props.books}
); const mapStateToProps = (state) => { return { books: getVisibleBooks(state.books, state.filters) }; } export default connect(mapStateToProps)(BookList);

-> mapStateToProps get books props from state.
-> connect() function get the function as parameter and apply to BookList Component to return a new connected React Component that can work with React state.

We can use connect() with one or more arguments depending on the use case:

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

Please visit React Redux API – connect for details.

We have 2 important arguments:

mapStateToProps(state) function connects a part of Redux state to React Component props.
The returned props of this function must be a plain object, which will be merged into the connected Component’s props, now it will have access to the exact part of Redux store.

mapDispatchToProps(dispatch) function is similar to mapStateToProps, but for actions. It connects Redux actions to React props. Therefore connected React Component can dispatch Redux actions.

Provider

In the example above, we use mapStateToProps to connect Redux state to React BookList Component props. But it’s not enough.

=> We have to do one more thing: make the Redux store available to the connect() call in the Component hierarchy below. We will wrap a parent or ancestor Component in Provider.

Provider is an high order component that wraps up React application and makes it aware of the entire Redux store. That is, it provides the store to its child components.

So if we want our entire React App to access the store, just put the App Component within Provider.

// app.js
import { Provider } from 'react-redux';

const template = (
    
        
    
);

// AppRouter.js
const AppRouter = () => (
                ...
                
                
);

// DashBoard.js
const DashBoard = () => (
    
);

Once we connected Redux state to React Component props, every time state is updated, props changes.

Example Overview

Goal

We will build a React Application using React Router v4 and Redux. It can get App state data and display them in a Component:

react-redux-example-goal

Project Structure

We have 2 main parts:

1- Redux

With our desired state like:

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
    }
};

We will have folder tree like this:

react-redux-example-organize-redux-structure

– Redux Actions and Reducers are separated in actions and reducers.
stores folder contains Redux Store Component which is created from result of combineReducers(books, filters).
selectors/book.js exports a functions that returns list of Book items after filtering and sorting.

2- React Components

The folder structure is just like React project that we have learned before:

react-redux-example-React-project-structure

AppRouter for implementing React Router.
DashBoard, AddBook, EditBook, Help, NotFound are React Components that will work with Router.
BookList Component is a child Component of DashBoard. In this Component, we use react-redux connect() function. Then props that is gotten from state will be used in Book Component – child of BookList Component.
– We will use react-redux Provider inside app.js.

3- React Redux App Structure

Our project structure is combination of 2 folder structure above:

react-redux-example-project-structure

Practice

Setup environment
Install Packages

– Open package.json:

{
  ...
  "author": "JavaSampleApproach",
  "scripts": {
    "serve": "live-server public",
    "build": "webpack",
    "dev-server": "webpack-dev-server"
  },
  "dependencies": {
    "babel-cli": "6.24.1",
    "babel-core": "6.25.0",
    "babel-loader": "7.1.4",
    "babel-preset-env": "1.6.1",
    "babel-preset-react": "6.24.1",
    "babel-plugin-transform-object-rest-spread": "6.26.0",
    "react": "16.3.0",
    "react-dom": "16.3.0",
    "react-modal": "3.3.2",
    "react-router-dom": "4.2.2",
    "webpack": "4.4.1",
    "webpack-cli": "2.0.13",
    "webpack-dev-server": "3.1.1",
    "style-loader": "0.20.3",
    "css-loader": "0.28.11",
    "sass-loader": "6.0.7",
    "node-sass": "4.8.3",
    "redux": "3.7.2",
    "uuid": "3.2.1",
    "react-redux": "5.0.7"
  }
}

Run cmd yarn install.

– Add plugin to .babelrc:

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

Open webpack.config.js:

const path = require('path');

module.exports = {
    ...
    module: {
        rules: [...]
    },
    devtool: 'cheap-module-eval-source-map',
    devServer: {
        contentBase: path.join(__dirname, 'public'),
        historyApiFallback: true
    }
};

historyApiFallback option is specifically for webpack-dev-server. Setting it true will effectively ask the server to fallback to index.html when a requested resource cannot be found (404 occurs).

Redux
Create Redux Actions

actions/books.js:

import uuid from 'uuid';

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

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

actions/filters.js:

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

export const startYear = (startYear) => ({
    type: 'START_YEAR',
    startYear
});

export const endYear = (endYear) => ({
    type: 'END_YEAR',
    endYear
});

export const sortBy = (sortType) => ({
    type: 'SORT_BY',
    sortType
});

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

export const clear = () => ({
    type: 'CLEAR',
    defaultFilter: filtersReducerDefaultState
});
Create Redux Reducers

reducers/books.js:

const booksReducerDefaultState = [];

export default (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;
    }
};

reducers/filters.js:

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

export default (state = filtersReducerDefaultState, action) => {
    switch (action.type) {
        case 'FILTER_TEXT':
            return {
                ...state,
                text: action.text
            };
        case 'START_YEAR':
            return {
                ...state,
                startYear: action.startYear
            };
        case 'END_YEAR':
            return {
                ...state,
                endYear: action.endYear
            };
        case 'SORT_BY':
            return {
                ...state,
                sortBy: action.sortType
            };
        case 'CLEAR':
            return {
                ...state,
                text: action.defaultFilter.text,
                sortBy: action.defaultFilter.sortBy,
                startYear: action.defaultFilter.startYear,
                endYear: action.defaultFilter.endYear
            };
        default:
            return state;
    }
}
Create Redux Store

store/store.js:

import { createStore, combineReducers } from "redux";
import booksReducer from '../reducers/books';
import filtersReducer from '../reducers/filters';

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
    }
};

export default () => {
    return createStore(
        combineReducers({
            books: booksReducer,
            filters: filtersReducer
        }
    ));
};
Create Selector for filtering and sorting

selectors/books.js:

// getVisibleBooks
export default (books, { text, sortBy, startYear, endYear }) => {
    return books.filter(book => {
        const textMatch =
            book.title.toLowerCase().includes(text.toLowerCase()) ||
            book.description.toLowerCase().includes(text.toLowerCase());

        const startYearMatch = typeof startYear !== 'number' || startYear <= book.published;
        const endYearMatch = typeof endYear !== 'number' || book.published <= endYear;

        return textMatch && startYearMatch && endYearMatch;
    }).sort((book1, book2) => {
        if (sortBy === 'title') {
            return book1.title.localeCompare(book2.title);
        } else if (sortBy === 'published') {
            return book1.published < book2.published ? -1 : 1;
        }
    });
}
React
Create Components

- For each Page that displays when clicking on Navigation item, we add one Component. So we have 4 Components: Dashboard, AddBook, EditBook, Help.
- We need a Component for 404 status, it's called NotFound.
- Now, we put a group of NavLink in header of Header Component:
Header.js

import React from 'react';
import { NavLink } from 'react-router-dom';

const Header = () => (
    

Java Sample Approach

Book Mangement Application

Dashboard Add Book Help
); export default Header;
Connect React Components with Redux

- Dashboard has BookList as a child:
DashBoard.js

import React from 'react';
import BookList from './BookList';

const DashBoard = () => (
    
); export default DashBoard;

- In BookList Component, we use react-redux connect() function:
BookList.js

import React from 'react';
import { connect } from 'react-redux';
import Book from './Book';
import getVisibleBooks from '../selectors/books';

const BookList = (props) => (
    
Book List:
    {props.books.map(book => { return (
  • ); })}
); const mapStateToProps = (state) => { return { books: getVisibleBooks(state.books, state.filters) }; } export default connect(mapStateToProps)(BookList);

- props that is gotten from state will be used in Book Component - child of BookList Component:
Book.js

import React from 'react';

const Book = ({ title, description, author, published }) => (
    

{title} ({published})

Author: {author}

); export default Book;
Create Router

Inside routers/AppRouter.js:

import React from 'react';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import Header from '../components/Header';
import DashBoard from '../components/DashBoard';
import AddBook from '../components/AddBook';
import EditBook from '../components/EditBook';
import Help from '../components/Help';
import NotFound from '../components/NotFound';

const AppRouter = () => (
    
        
); export default AppRouter;
Run and Check Result
app.js

- get store.
- dispatch addBook action after every second (we want to check if Component props is updated whenever state changes).
- use react-redux Provider to make Redux store available for connecting with React Components.

import React from 'react';
import ReactDOM from 'react-dom';
import AppRouter from './routers/AppRouter';
import getAppStore from './store/store';
import { addBook } from './actions/books';
import { filterText, startYear, endYear, sortBy, clear } from './actions/filters';
import getVisibleBooks from './selectors/books';
import './styles/styles.scss';

import { Provider } from 'react-redux';

const store = getAppStore();

setTimeout(() => {
    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
    }))
}, 1000);

setTimeout(() => {
    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
    }))
}, 2000);

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

const template = (
    
        
    
);

ReactDOM.render(template, document.getElementById('app'));
Check Result

react-redux-example-result

Source Code

ReactRedux

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

More Practice

Add remove Button for each Item

Using react-redux connect(), we can call Redux dispatch() function directly:
Book.js

import React from 'react';
import { connect } from 'react-redux';
import { removeBook } from '../actions/books';

const Book = ({ id, title, description, author, published, dispatch }) => (
    

{title} ({published})

Author: {author}

); export default connect()(Book);
Source Code

ReactRedux-addRemoveButton



By grokonez | April 17, 2018.

Last updated on August 23, 2018.



Related Posts


3 thoughts on “How to connect React with Redux – react-redux example”

  1. Thank you very much. I saw alot of examples that didn’t even work. This works out of the box. And it uses Yarn, Scss and React-Router-v4!

  2. this would be much better without sass – which always fails to compile it’s native bindings for me.

    “`
    Downloading binary from https://github.com/sass/node-sass/releases/download/v4.8.3/darwin-x64-64_binding.node
    Cannot download “https://github.com/sass/node-sass/releases/download/v4.8.3/darwin-x64-64_binding.node”:

    HTTP error 404 Not Found

    Hint: If github.com is not accessible in your location
    try setting a proxy via HTTP_PROXY, e.g.

    export HTTP_PROXY=http://example.com:1234

    or configure npm proxy via

    npm config set proxy http://example.com:8080

    “`

    followed by a whole bunch of make errors.

Got Something To Say:

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

*