Redux is a popular library for React application state management!

In this article, let’s talk

  • How do I use Typescript to manage Redux state in a strongly typed manner
  • How do I use react-redux Hooks to make the React component interact with the Redux state

Install dependencies

yarn add redux react-redux

yarn add @types/react-redux --dev

#Install the Redux developer tools
$ yarn add redux-devtools --dev
Copy the code

Let’s use Redux to add and remove a user name. To deepen our understanding of Redux

State

We use the following types as the state types of stores

// src/redux/data.d.ts

// The field type of the user
type Person = {
  id: number;
  name: string;
};

// All user types
type AppState = {
  people: Person[];
};
Copy the code

As you can see, our application state is a people, which is an array type containing id/name

We kept the state as simple as possible because we focused on the Redux Store and how React components interact in a strongly typed way.

Actions and Actions create functions

As we all know, to change a state in Redux, you must initiate an action, and the action must include

  • What is the action initiated
  • What does the initiated action need to pass

There are two actions in our example

  • AddPerson: Triggered when a user name is added. This action contains the name of the new user
  • RemovePerson: Triggered when a user name is deleted. This action contains the user’sid

Next up is the action creation function, which creates and returns an Action object. Next up is the action creation function we implemented for the two actions

// actionTypes
enum actionTypes {
  ADD_PERSON = 'ADD_PERSON',
  REMOVE_PERSON = 'REMOVE_PERSON',}export default actionTypes;

Copy the code
// src/redux/actions/index.ts

import actionTypes from "./actionTypes";

export const addPerson = (personName: string) = > {
  return {
    type: actionTypes.ADD_PERSON,
    payload: personName,
  } as const;
};

export const removePerson = (id: string) = > {
  return {
    type: actionTypes.REMOVE_PERSON,
    payload: id,
  } as const;
};
Copy the code

Note that we did not give payload an explicit type, because the action creation function can automatically infer what type it is.

Reducer

The reducer is a function that receives the state parameter and action and uses it to update state.

First we define the Typescript type for the action parameter

type Actions = ReturnType<typeof addPerson> | ReturnType<typeof removePerson>;
Copy the code

This is the union type for all actions. We use the Typeof keyword to get the types of the functions created by the action, and then we use ReturnType to get the return types of those functions. With this approach, we do not need to explicitly create a type for the Action object.

The reducer function is shown as follows

import { addPerson, removePerson } from ".. /actions/index";
import type { Person } from ".. /data.d";
import actionTypes from ".. /actions/actionTypes";

type Actions = ReturnType<typeof addPerson> | ReturnType<typeof removePerson>;

const initialState: Person[] = [{ id: "1".name: "Little girl" }];

export default function peopleReducer(state = initialState, action: Actions) {
  switch (action.type) {
    case actionTypes.ADD_PERSON:
      return state.concat([
        {
          id: (Math.random() * 1000000).toFixed(0),
          name: action.payload,
        },
      ]);
    case actionTypes.REMOVE_PERSON:
      return state.filter((person) = >person.id ! == action.payload);default:
      break;
  }
  return state;
}
Copy the code

We explicitly set the type of the function argument and do not specify the type of the return value (as determined by type inference of TS).

Note that the action.type in the switch statement is strongly typed, so if we enter the wrong value in the case, an error will be raised.

Store

We use the createStore in Redux to create a function that generates a store

import { combineReducers, createStore } from "redux";
import { Store } from "redux";
import { AppState } from "./data.d";

import peopleReducer from "./reducers/index";

const rootReducer = combineReducers<AppState>({
  people: peopleReducer,
});

function configureStore() :Store<AppState> {
  const store = createStore(rootReducer, undefined);
  return store;
}

const storeData = configureStore();

export default storeData;
Copy the code

Create a rootReducer using the combinedReducers function in Redux

Setting the type of store is also simple: use the store generic type in Redux, which in our case is AppState:

Link component

Finally to the component step, <( ̄ ▽  ̄)/

First we need to wrap the top-level component with the Provider component in React-Redux, and then pass the Store to the Provider component:

import { Provider } from "react-redux";
import store from './redux/store'

const App = () = > (
  <Provider store={store}>
    <Page />
  </Provider>
);
Copy the code

Inside the child component, we can use the React Redux useSelector hook to get data from the Store

// src/pages/home/homeByFunc.tsx

import React from "react";
import { useSelector } from 'react-redux'
import type { Person, AppState } from "@/redux/data.d"

const Index: React.FC = () = >{...const people: Person[] = useSelector((state: AppState) = >state.people); . }Copy the code

The parameter received by the useSelector function is also a function that gets the state from the store and returns the relevant data. The type of the state parameter is explicitly set using the AppState type.

Next, we use the React Redux useDispatch hook to call the action:

// src/pages/home/homeByFunc.tsx

import React from "react";
import { useDispatch } from 'react-redux'

const Index: React.FC = () = >{...constdispatch = useDispatch(); . }Copy the code

UseDispatch returns a function we call Dispatch. We then trigger the action by passing the action creation function to Dispatch:

// src/pages/home/homeByFunc.tsx
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) = >{ e.preventDefault(); dispatch(addPerson(newPerson)); . };const dispatchNewPerson = (id: number) = > () = >{ dispatch(removePerson(id)); }; . <button onClick={dispatchNewPerson(person.id)}>Remove</button>Copy the code

Is this the end? 🙅♂️ No, it’s a little far from building a full React app. So in the next article, I’ll show you how to implement asynchronous Redux in your projects