Tips & Tricks for your React-Redux Application

Tips & Tricks for your React-Redux Application

<img src="https://cdn-images-1.medium.com/max/800/1*4877k4Hq9dPdtmvg9hnGFA.jpeg">

Please note: This assumes you are familiar with the flow of React & Redux. We are using the redux-saga middleware in our example. The focus of this article is more about boilerplate and not styling.

If you are accustomed to building React applications with Redux, you’ll know there is a lot of associated boilerplate. When implementing a feature, it usually involves the following steps.

  1. Build a component and determine the state/data that is needed for our component to function correctly.
  2. Once we have the state we need, we map our Component’s state to props passed from the store.
  3. To get that state — in our case it’s a collection of users — we’ll need to call an API endpoint provided from the backend guys. This means we need to trigger an action with a dispatch in our Component.
  4. Our Redux saga middleware listens for this action and then makes a request to our API service to fetch the users when the action has been fired.
  5. When the saga has finished making the call and it was successful, it will trigger an effect with a new success action. The reducer will then update the store with the people list. The Component is now aware of the change of props and will re-render with the new state retrieved.

We created a simple create-react-app for this ‘People’ application. It will render a list of people. Simplez… (Try to make the sound the meerkat makes on the adverts but fail miserably).

We added a Header and Panel component, but our focus is on a component called PeopleList that will just render an array of people to the browser. While we are waiting for the data from the API, we’ll show a loading animation.

Please note: We structure our components in the following way.

src/components/Component/Component.js
src/components/Component/Component.css
src/components/Component/index.js

This allows us to break apart the connection with Redux and test the component in isolation. Now onto the component implementation.

src/components/PeopleList/PeopleList.js

import React, { Component } from 'react';
import Loading from 'react-loading';
import {
  shape, arrayOf, string, func, bool, number,
} from 'prop-types';
import './PeopleList.css';

import Panel from '../Panel';

import { getPeople } from '../../actions';

class PeopleList extends Component { componentDidMount() { const { fetchPeople } = this.props;

fetchPeople();

}

showUsers() { const { people } = this.props;

return people.map(person =&gt; (
  &lt;div key={person.id} className="person"&gt;
    &lt;img
      alt="person"
      className="avatar"
      src={`https://ui-avatars.com/api/?name=${person.name}`}
    /&gt;
    &lt;div className="bio"&gt;{person.name}&lt;/div&gt;
  &lt;/div&gt;
));

}

render() { const { hasLoadedPeople } = this.props; if (hasLoadedPeople === false) { return <Loading className="loading" color="#14233c" />; } return ( <Panel size="md"> <div className="people-list">{this.showUsers()}</div> </Panel> ); } }

PeopleList.propTypes = { people: arrayOf( shape({ id: number, name: string, }), ), hasLoadedPeople: bool.isRequired, fetchPeople: func.isRequired, };

PeopleList.defaultProps = { people: [], };

export const mapStateToProps = ({ people: { list, hasLoadedPeople } }) => ({ people: list, hasLoadedPeople, });

export const mapDispatchToProps = { fetchPeople: getPeople.request, };

export default PeopleList;

Now we connect it to redux in the index file.

components/PeopleList/index.js

import { connect } from 'react-redux';
import PeopleList, { mapStateToProps, mapDispatchToProps } from './PeopleList';

export default connect( mapStateToProps, mapDispatchToProps, )(PeopleList);

Next thing we will do is define our action types.

export const GET_PEOPLE = 'GET_PEOPLE';

Now usually we would define an action type for each state of a request when making an API call. Like so:

export const GET_PEOPLE = 'GET_PEOPLE';
export const GET_PEOPLE_REQUEST = 'GET_PEOPLE_REQUEST';
export const GET_PEOPLE_FAILURE = 'GET_PEOPLE_FAILURE';
export const GET_PEOPLE_SUCCESS = 'GET_PEOPLE_SUCCESS';

Then in the reducer, we’d usually define a switch case for each of these action types.

switch (action.type)
  case GET_PEOPLE_REQUEST:
  ... update state
  case GET_PEOPLE_FAILURE:
  ... update state
  case GET_PEOPLE_SUCCESS:
  ... update state with users

We can make use of redux-saga-routines to simplify this boilerplate, by doing the following.

Please note: In your own application you will need to

npm install redux-saga-routines

src/actions/index.js

import { createRoutine } from 'redux-saga-routines';

export const GET_PEOPLE = 'GET_PEOPLE';

export const getPeople = createRoutine(GET_PEOPLE);

Now getPeople will be an object that has all of these states. So we can simply import the routine alone instead of multiple action types.

src/reducers/people.js

import { getPeople } from '../actions';

const INITIAL_STATE = { hasLoadedPeople: false, errorGettingPeople: false, list: [], };

const people = (state = INITIAL_STATE, { type, payload }) => { switch (type) { case getPeople.REQUEST: return { ...state, list: [], hasLoadedPeople: false, errorGettingPeople: false, }; case getPeople.FAILURE: return { ...state, errorGettingPeople: true, }; case getPeople.SUCCESS: return { ...state, hasLoadedPeople: true, list: payload.people, }; default: return state; } };

export default people;

The problem with having a large switch case in your reducers like above is that you may have issues with redeclaring variables or constants in the same lexical scope. The linter could also complain as you do to that neighbour with the dog that won’t stop barking all night.

We can make use of something called handleActions that will give each action its own lexical scope to avoid clashing with the other actions.

Please note: In your own application you will need to

npm install redux-actions

src/reducers/people.js (New version)

import { handleActions } from 'redux-actions';

import { getPeople } from '../actions';

const INITIAL_STATE = { hasLoadedPeople: false, errorGettingPeople: false, list: [], };

const people = handleActions( { [getPeople.REQUEST]: state => ({ ...state, list: [], hasLoadedPeople: false, errorGettingPeople: false, }), [getPeople.FAILURE]: state => ({ ...state, errorGettingPeople: true, }), [getPeople.SUCCESS]: (state, { payload }) => ({ ...state, hasLoadedPeople: true, list: payload.people, }), }, INITIAL_STATE, );

export default people;

Now we have cleaned up our reducer and action, we can check out our saga. You can see below we import the getPeople routine, and it has a function called success and failure. At the bottom, you can see we are listening for a REQUEST, and then firing the getPeopleRequest generator function which in turn calls the service and gets us back the data we need.

src/sagas/people.js

import { call, put, takeEvery } from 'redux-saga/effects';

import API from '../services/api'; import { getPeople } from '../actions';

export function* callPeopleService() { return yield call(API.getPeople); }

export function* getPeopleRequest() { try { const { data } = yield call(callPeopleService); yield put( getPeople.success({ people: data, }), ); } catch (error) { yield put(getPeople.failure(error.message)); } }

export default function* peopleSaga() { yield takeEvery(getPeople.REQUEST, getPeopleRequest); }

The API service is simply calling a Fake API on the web. Here it is for clarity.

src/services/api.js

The service below injects axios (The library used to make HTTP requests) through the constructor. This makes it much easier to mock in unit tests. This is an approach known as dependency injection.

import axios from 'axios';

const GET_PEOPLE_ENDPOINT = https://jsonplaceholder.typicode.com/users;

class API { constructor(api) { this.api = api; }

getPeople = () => { return this.api.get(GET_PEOPLE_ENDPOINT); }; }

export default new API(axios);

When I run the application, I see my redux saga routine in action and the store being updated using the Redux Dev tools.

Hope you enjoyed reading along. The source code for this project can be found here. Please drop a few claps if you enjoyed and leave feedback or any questions you may have. Thanks!


By : Shaun Michael Stone







Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

What's new in Bootstrap 5 and when Bootstrap 5 release date?

Brave, Chrome, Firefox, Opera or Edge: Which is Better and Faster?

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

An intro to Redux and how state is updated in a Redux application

I started learning Redux a few days back and it was an overwhelming concept for me at the start. After polishing my skills in ReactJS by making a&nbsp;<a href="https://github.com/aimenbatool/my-reads" target="_blank">personal book reading application</a>, I headed towards Redux to learn more about it.

React Boilerplates with Redux, Redux Saga and Best Practices

React Boilerplates with Redux, Redux Saga and Best Practices