Setting up Redux store in Your React application
Tags: react, redux, webdev
Published: 2020-11-14 11:04:32

State management is one of the most important portion of any Front end development framework. Almost every FE framework offers one or many state management libraries. For example,  [Redux](https://github.com/reduxjs/redux) &  [Recoil](https://recoiljs.org/docs/introduction/installation)  for React,  [Vuex](https://github.com/vuejs/vuex) for VueJS and  [NgRx](https://ngrx.io/guide/store) for Angular. In this article, we're going to create a very simple Reading List application which will have a redux store set up and we will be using  [FakerAPI](https://fakerapi.it/en) for the mock response. 

You can checkout the demo application  [Here](https://reading-list-redux.vercel.app) .
Also, the source-code can be found  [Here](https://github.com/rajandmr/Reading-List-Redux) on my GitHub. It is very basic application which fetches books from FakerAPI and you'll also be able to add Books.

> Prerequisite

I assume that you already have a good understanding of React Components, Props & states(in general). 

> Getting Started

So we start simple by creating a React application using CRA and installing required dependencies afterwards.

```
create-react-app reading-list
```

This will generate the react application. Now, navigate into the newly created application and install the dependencies using

```
cd reading-list
npm install redux react-redux redux-thunk redux-devtools-extension axios
```

Now, few things to note here, redux alone is independent of any frameworks. *react-redux* is what let us use redux for react application. Also, we need some kind of middleware, *redux-thunk* in our case for basic Redux side effects logic, including complex synchronous logic that needs access to the store, and simple async logic like AJAX requests since With a plain basic Redux store, you can only do simple synchronous updates by dispatching an action. Middleware extends the store's abilities, and lets you write async logic that interacts with the store.

Also, *redux-devtools-extension* makes it easy to integrate *Redux DevTools* which speeds up our app debugging process. *Axios* works great for fetching data from the apis.

> Folder Structure

Now, let's take a look at our folder structure

![folder-structure.JPG](https://cdn.hashnode.com/res/hashnode/image/upload/v1605350225381/Q0tA1Wdgm.jpeg)

We'll create 3 folders *actions* , *components* & *reducers* inside our *src* folder. Inside the components folder, we'll create 3 components, *BookList* for looping through the List of Books, *BookForm* for adding a new Book & *BookDetail* for displaying each book's Detail.

Inside *reducers* folder, we'll have 2 files, *index.js* which will be our rootReducer & *bookReducer*.

> Setting up the store

In order to set up the store, replace the *src/index.js* file with

```
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

// Imports for Redux
import { createStore, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';

// root reducer import
import rootReducer from './reducers';

const store = createStore(
  rootReducer,
  composeWithDevTools(applyMiddleware(thunk))
);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
```

> Creating components

Since the store has been set up, we can start writing our components. Add the following code to build up our components:

```
// src/components/BookDetail.js
import React from 'react';

const BookDetails = ({ book }) => {
  return (
    <li>
      <div className="title">{book.title}</div>
      <div className="author">{book.author}</div>
    </li>
  );
};

export default BookDetails;
```

```
// src/components/BookForm.js
import React, { useState } from 'react';
import { connect } from 'react-redux';
import { addBook } from '../actions/bookActions';

const BookForm = ({ dispatch }) => {
  const [title, setTitle] = useState('');
  const [author, setAuthor] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    const newBook = {
      title,
      author,
      id: 5,
    };
    dispatch(addBook(newBook));
    setTitle('');
    setAuthor('');
  };
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="book title"
        value={title}
        onChange={(e) => setTitle(e.target.value)}
        required
      />
      <input
        type="text"
        placeholder="author"
        value={author}
        onChange={(e) => setAuthor(e.target.value)}
        required
      />

      <input type="submit" value="add book" />
    </form>
  );
};

export default connect(null)(BookForm);
```

```
// src/components/BookList.js

import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import BookDetails from './BookDetails';

import { fetchBooks } from '../actions/bookActions';

const BookList = ({ dispatch, books }) => {
  useEffect(() => {
    dispatch(fetchBooks());
  }, [dispatch]);
  return books.length ? (
    <div className="book-list">
      <ul>
        {books.map((book) => {
          return <BookDetails book={book} key={book.id} />;
        })}
      </ul>
    </div>
  ) : (
    <div className="empty">No books to read</div>
  );
};

const mapStateToProps = (state) => ({
  books: state.books.books,
});

export default connect(mapStateToProps)(BookList);
```

> Setting up Reducers

Reducers are what responsible for mutating states. It reads the dispatched action type and mutates the state accordingly. There is one main reducer generally called rootReducer which keeps track of all the other reducers. If you look at *src/index.js*, in **createStore** method, we only passed on Reducer which is root Reducer. The rootReducer contains all the other reducers.

Add the following code in *src/reducers/index.js*

```
import { combineReducers } from 'redux';

import booksReducer from './booksReducer';

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

export default rootReducer;
```
We see that it is calling *combineReducers* method from redux and taking all the other reducers.

Add the following code to *src/reducers/bookReducer.js*
```
import { GET_BOOKS, ADD_BOOK } from '../actions/bookActions';

export const initialState = {
  books: [],
};

export default function bookReducer(state = initialState, action) {
  switch (action.type) {
    case GET_BOOKS:
      return {
        ...state,
        books: action.payload,
      };

    case ADD_BOOK:
      return {
        ...state,
        books: [...state.books, action.payload],
      };

    default:
      return state;
  }
}
```

> Setting up actions

Add the following code to *src/actions/bookActions.js*

```
import Axios from 'axios';
export const GET_BOOKS = 'GET_BOOKS';
export const ADD_BOOK = 'ADD_BOOk';

export const fetchBooks = () => async (dispatch) => {
  const data = await fetchData();
  dispatch({
    type: GET_BOOKS,
    payload: data,
  });
};

export const addBook = (newBook) => async (dispatch) => {
  dispatch({
    type: ADD_BOOK,
    payload: newBook,
  });
};

// fetch data from the API
const fetchData = async () => {
  try {
    const res = await Axios.get(
      'https://fakerapi.it/api/v1/custom?_quantity=5&author=name&id=counter&title=city'
    );

    return res.data.data;
  } catch (error) {
    console.log(error);
  }
};
```

> Styling

Since we're mainly focused on setting up redux, it doesn't mean our application has to look ugly. That's why i've already written some basic styling which will make our app look decent.

Replace all the codes in *src/index.css* with following

```
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
    'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  background: #553055;
}
.App {
  background: #4c2a4c;
  margin: 20px auto;
  width: 90%;
  max-width: 700px;
  color: #eee;
}
.navbar {
  padding: 10px 20px;
  text-align: center;
  background: #6d3d6d;
}
.navbar h1 {
  margin: 10px 0;
}

.book-list {
  margin: 20px;
}
.book-list ul {
  padding: 0;
  list-style-type: none;
}
.book-list li {
  background: #6d3d6d;
  border-radius: 4px;
  padding: 10px;
  cursor: pointer;
  margin: 10px 0;
}
.book-list li:hover {
  opacity: 0.7;
  text-decoration: line-through;
}
.book-list .title {
  font-weight: bold;
  color: #fff;
  font-size: 1.2em;
}
.book-list .author {
  font-size: 0.9em;
  color: #ddd;
}
.empty {
  margin: 20px;
  text-align: center;
}

form {
  padding: 20px;
}
input[type='text'] {
  width: 100%;
  padding: 10px;
  box-sizing: border-box;
  margin: 6px 0;
  background: #3c1f3c;
  color: #fff;
  border: 0;
}
input[type='submit'] {
  margin: 10px auto;
  background: #eee;
  border: 0;
  padding: 6px 20px;
  display: block;
}
```

Now, finally, let's add our components to *src/App.js* . Replace all the codes in *src/App.js* with following

```
import BookForm from './components/BookForm';
import BookList from './components/BookList';

function App() {
  return (
    <div className="App">
      <BookList />
      <BookForm />
    </div>
  );
}

export default App;
```

Now, if you've followed everything accordingly, once you start the server, you can see the application running. Also if you look at *Redux DevTools* you'll be able to see how the states changed, what actions were launched.

If you run in any problem, you can always use the code from  [Here](https://github.com/rajandmr/Reading-List-Redux) as a reference.