DEV Community

loading...
Cover image for Setting up Redux store in Your React application

Setting up Redux store in Your React application

rajandmr profile image Rajan Prasad ・5 min read

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 & Recoil for React, Vuex for VueJS and NgRx 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 for the mock response.

You can checkout the demo application Here .
Also, the source-code can be found Here 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

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')
);
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode
// 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);
Enter fullscreen mode Exit fullscreen mode
// 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);
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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);
  }
};
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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 as a reference.

Discussion (2)

pic
Editor guide
Collapse
markerikson profile image
Mark Erikson

Hi, I'm a Redux maintainer. As an FYI, we now recommend several newer patterns than what's shown in this post. In particular, you should be using our official Redux Toolkit package to set up the store and write your Redux logic, and we recommend using a "feature folder" structure with "slice" files for logic rather than splitting files across multiple folders.

I strongly recommend reading through the new newly rewritten official tutorials in the Redux docs, which have been specifically designed to teach you how Redux works and show modern practices:

  • "Redux Essentials" tutorial: teaches "how to use Redux, the right way", by building a real-world app using Redux Toolkit
  • "Redux Fundamentals" tutorial: teaches "how Redux works, from the bottom up", by showing how to write Redux code by hand and why standard usage patterns exist, and how Redux Toolkit simplifies those patterns

The older patterns shown in almost all other tutorials on the internet are still valid, but not how we recommend writing Redux code today.

You should also read through the Redux "Style Guide" docs page, which explains our recommended patterns and best practices. Following those will result in better and more maintainable Redux apps.

In addition, the easiest way to start a new project is with the official Redux+JS template for Create-React-App. It comes with Redux Toolkit and the React-Redux hooks API already set up when the project is created.