Build a Type-Safe React Redux App with Ease

The Power of Type Safety

React is an excellent library for building modern frontends, but as your application grows, managing data becomes increasingly complex. That’s where Redux comes in – a state management library that helps you keep your app’s state organized. By combining React with Redux and TypeScript, you can create a robust and scalable application.

Creating a Sample Ecommerce App

Let’s build a sample ecommerce app with two main domains: inventory and cart. We’ll create a type-safe Redux app, set up a React Redux folder structure, and implement Redux in a shopping cart.

Setting Up the Redux Store

First, we need to create the essential Redux building blocks: action creators, reducers, and stores. We’ll structure our app based on the domain, using a feature folder pattern to keep our code organized.


// store.ts
import { createStore, combineReducers } from 'edux';
import inventoryReducer from './inventory/reducer';
import cartReducer from './cart/reducer';

const rootReducer = combineReducers({
  inventory: inventoryReducer,
  cart: cartReducer,
});

const store = createStore(rootReducer);

export default store;

Inventory Domain: Actions, Reducers, and Types

Next, we’ll create actions, reducers, and types for the inventory domain. We’ll define the Inventory interface, determine action types, and handle the type of domain state.


// inventory/types.ts
interface Inventory {
  id: number;
  name: string;
  quantity: number;
}

enum ActionTypes {
  FETCH_INVENTORY_SUCCESS,
  FETCH_INVENTORY_FAILURE,
}

interface FetchInventorySuccessAction {
  type: ActionTypes.FETCH_INVENTORY_SUCCESS;
  payload: Inventory[];
}

interface FetchInventoryFailureAction {
  type: ActionTypes.FETCH_INVENTORY_FAILURE;
  error: string;
}

Using Redux Thunk for API Calls

We’ll use Redux Thunk as a middleware to make API calls. Redux Thunk turns the action into a function that makes an API call and dispatches an action.


// inventory/actions.ts
import { ActionTypes } from './types';
import { fetchInventoryAPI } from '../../api';

export function fetchInventory() {
  return async (dispatch: Dispatch) => {
    try {
      const response = await fetchInventoryAPI();
      dispatch({
        type: ActionTypes.FETCH_INVENTORY_SUCCESS,
        payload: response.data,
      });
    } catch (error) {
      dispatch({
        type: ActionTypes.FETCH_INVENTORY_FAILURE,
        error: error.message,
      });
    }
  };
}

Implementing Redux in a Shopping Cart

Now it’s time to implement Redux functionalities for the cart domain. We’ll create actions, reducers, and types for the cart, using Redux Thunk to make API calls.


// cart/types.ts
interface CartItem {
  id: number;
  quantity: number;
}

enum ActionTypes {
  ADD_TO_CART_SUCCESS,
  ADD_TO_CART_FAILURE,
}

interface AddToCartSuccessAction {
  type: ActionTypes.ADD_TO_CART_SUCCESS;
  payload: CartItem;
}

interface AddToCartFailureAction {
  type: ActionTypes.ADD_TO_CART_FAILURE;
  error: string;
}

Configuring the Store

We’ll configure the store for our application, defining the type for arguments such as history and initialState.


// store.ts
import { createStore, combineReducers } from 'edux';
import { createBrowserHistory } from 'history';
import inventoryReducer from './inventory/reducer';
import cartReducer from './cart/reducer';

const history = createBrowserHistory();

const rootReducer = combineReducers({
  inventory: inventoryReducer,
  cart: cartReducer,
});

const initialState = {};

const store = createStore(rootReducer, initialState, history);

export default store;

Components Structure

Finally, we’ll set up our components, including the HomePage, Navbar, and Cart. Once you know how to structure a type-safe Redux application, implementing components is straightforward.


// components/HomePage.tsx
import React from 'eact';
import { connect } from 'eact-redux';
import { fetchInventory } from '../inventory/actions';

interface Props {
  inventory: Inventory[];
  fetchInventory: () => void;
}

const HomePage: React.FC = ({ inventory, fetchInventory }) => {
  return (

Home Page

    {inventory.map((item) => (

  • {item.name}
  • ))}


  );
};

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

export default connect(mapStateToProps, { fetchInventory })(HomePage);

What’s Next?

Now that you know how to build a type-safe Redux application, you may notice that it takes a lot of code to set up Redux. Fortunately, there’s a solution: Redux Toolkit. This package eases the process of implementing Redux, providing features like configureStore, createReducer, createAction, and createSlice.

Summary

Adding type checking to your React Redux app can help you avoid bugs at compile time, making your development process more efficient and enjoyable. By following this tutorial, you’ll learn how to create a type-safe React Redux app using Redux, Redux Thunk, and TypeScript.

Leave a Reply