How to use Redux with TypeScript ?
Tags: tutorial, typescript, react, redux
Published: 2021-04-03 02:49:25

In this small tutorial, I will show you how to use Redux with TypeScript. 

**Disclaimer:** I want to do this tutorial as a way to re-do what I learn and at the same time I want to share this knowledge with anyone who needs it. I can't make sure this is the best practice. But if you just want to find out how you can use redux with TypeScript this is one of them.

We will make a small application that simply reaches out to an API then fetches some data and dumps it out the screen. Now, this app may be small and that's okay because our main focuses are :

- How to setUp a react-typescript project
- How to setUp a redux store in our project
- How to create an actionCreator that will perform an Async/Await Request 
- How to use this actionCreator in our components.

This tutorial requires you to know : 
- React 
- Redux 
- ES6 (async/await) 
- Basic of TypeScript

At the end of this tutorial, I will include some useful links for anyone who wants to learn more about TypeScript. 

# Why TypeScript ? 
We know that TypeScript is rising :star: .Especially at the enterprise level where we have a lot of code to manage. People want to understand and see how data flow in their web applications. 

I mean JavaScript works just fine. There nothing wrong with JavaScript except for the fact that with JavaScript alone it is really hard for you to come into a legacy codebase to build a new feature and this feature includes dozens of files:dizzy_face: . All these files contain different variables, functions that are not written by you. So it will take you a lot of time to understand all of that without any directions whatsoever. 

That is where TypeScript comes in. It will define `type` for nearly everything. Plus this `type` thing is also a protection for the app itself and this protection will prevent you from passing in the wrong type and causing more errors that you don't even know about. 

The downside of TypeScript however is that it is somewhat re-invent the wheel (debatable). TypeScript does not help you build faster features especially if you are someone who doesn't understand TypeScript well enough. You can think of writing TypeScript as somewhat the same as writing tests.

You will have more errors than before because simply the app doesn't want to accept your type for some reason and you waste more time looking for the answer why :rage: .Believe me, It happens. 

But it is undeniable that If we choose to use TypeScript we are choosing to develop an app that will eventually have ten thousand lines of code :page_facing_up: and make our future easier by making it more maintainable. 

Plus as a developer. There are a lot of companies that desire a developer with experience with TypeScript :moneybag: :moneybag: :moneybag: .And that alone is a good reason to start learning it. 

# Tutorial : 

### Start our new react-typeScript project : 
You need to choose a directory where you want your app to be and then open a command line in that directory then type this in : 

```
npx create-react-app PROJECT_NAME --template typescript
```
Okay now go sipping some :coffee: :coffee: :coffee: waiting for the npm packages to do it things 

### Install packages : 
We will install some packages that allow us to work with both typeScript and redux. Open another command line in the project directory and type : 

```
npm install --save @types/react-redux axios react-redux redux redux-thunk 
```
We just install 5 packages, I will go through each and single one of them with you : 
- types/react-redux : for defining types for react-redux packages. When you use packages with typescript you have to download their `type` packages as well. 
- axios : this is for working with ajax easier. We actually don't need it but I love it so yea :satisfied: 
- react-redux : You need this to work connect redux to react 
- redux : Redux library itself 
- redux-thunk : You need this to create an actionCreator 

### Understand what we want to build : 
We gonna build a simple reducer that has an initialState of 3 things: 
- comments: Comment[]
- loading : boolean
- error : string | null 

We will reach out to an API and fetch the comments array of that post with our input as postId then dump all comments out to the screens. 

This is the API we will use : `https://jsonplaceholder.typicode.com/comments?postId=1` .The postId will be inserted from an input that we gonna build later.

(PICTURE DEMO HERE)

### SetUp Our Reducer 
This should be your project folder structure at the moment : 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0nxkqdxxcux382gj3d6e.png)
 
Go and create a folder structure like this : 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rg1xmtp7ftm008pr3sxl.png)

This is what your normal reducer looks like : 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4hcwz5jlrexoz42i07ov.png)

This won't work in TypeScript because we did not define a type for both state and action of this reducer.Now we will define a type for our state first : 
```typescript 
interface Comment {
    postId: number,
    id: number,
    name: string,
    email: string,
    body: string
}

interface State {
    comments: Comment[],
    loading: boolean,
    error: string | null 
}
```
Pretty normal right ? Next we will create a type for our actions. 
The thing with our actions is that based on action.type we will have different kind of payloads. In this case we have 3 different action.types so we need 3 different types of action for this. 
```typescript
enum ActionType {
    GET_POST_COMMENTS_PENDING = 'GET_POST_COMMENTS_PENDING',
    GET_POST_COMMENTS_SUCCESS = 'GET_POST_COMMENTS_SUCCESS',
    GET_POST_COMMENTS_FAIL = 'GET_POST_COMMENTS_FAIL'
}

interface actionPending {
    type: ActionType.GET_POST_COMMENTS_PENDING;
}

interface actionSuccess {
    type: ActionType.GET_POST_COMMENTS_SUCCESS;
    payload: string[];
}

interface actionFail {
    type: ActionType.GET_POST_COMMENTS_FAIL;
    payload: string ;
}

type Action = actionPending | actionSuccess | actionFail;

export const commentReducer = (state: State = initialState, action: Action):State => {
...}
```

I know that our code in this file is messy but It's okay. We will re-factor them later. That's why in the folder redux structure you see I create 2 other folders. 

But before close this chapter, I need you to create a compbine File in your reducers to combine all reducers like this : 
```typescript
import { combineReducers } from 'redux';
import { commentReducer } from './index.ts';

const reducers = combineReducers({
comments: commentReducer
});

export default reducers;
//This RootState is required to use useSelector later on 
export type RootState = ReturnType<typeof reducers>;
```

At this point we already have our reducer ready. 

### Create an actionCreator and re-factor code :
Create a new file in dir: 'actionTypes' in redux dir. Then put all these codes in there and export our Action : 
```typescript 
import { Comment } from '../reducers/index';

export enum ActionType {
    GET_POST_COMMENTS_PENDING = 'GET_POST_COMMENTS_PENDING',
    GET_POST_COMMENTS_SUCCESS = 'GET_POST_COMMENTS_SUCCESS',
    GET_POST_COMMENTS_FAIL = 'GET_POST_COMMENTS_FAIL'
}

interface actionPending {
    type: ActionType.GET_POST_COMMENTS_PENDING;
}

interface actionSuccess {
    type: ActionType.GET_POST_COMMENTS_SUCCESS;
    payload: Comment[];
}

interface actionFail {
    type: ActionType.GET_POST_COMMENTS_FAIL;
    payload: string ;
}

export type Action = actionPending | actionSuccess | actionFail;
```
Then import our Action and ActionTypes in our reducer File and replace the action.types the put some info in return statement and That's it. This is what our reducer currently looks like : 
```typescript
import { Action, ActionType } from '../actionTypes/index';

interface Comment {
    postId: number,
    id: number,
    name: string,
    email: string,
    body: string
}

interface State {
    comments: Comment[];
    loading: boolean;
    error: string | null;
}

const initialState = {
    comments: [],
    loading: false, 
    error: null 
}

export const commentReducer = (state: State = initialState, action: Action):State => {
    switch(action.type) {
        case ActionType.GET_POST_COMMENTS_PENDING:
            return {
                loading: true 
            } 
        case ActionType.GET_POST_COMMENTS_SUCCESS:
            return {
                loading: false,
                comments: action.payload
            }
        case ActionType.GET_POST_COMMENTS_FAIL:
            return {
                loading: false,
                error: action.payload 
            }
        default: 
            return state;
    }
}
```

Now we will create an actionCreator, if you have create an actionCreator before, then do it yourself then compare with code below :
```typescript 
import axios from 'axios';
import { Dispatch } from 'redux';
import { ActionType, Action } from '../actionTypes';

export const getComments = (postId: string) => {
    return async (dispatch: Dispatch<Action>) => {
        dispatch({
            type: ActionType.GET_POST_COMMENTS_PENDING
        });

        try {
            const { data } = await axios.get(`https://jsonplaceholder.typicode.com/comments?postId=${postId}`);

            dispatch({
                type: ActionType.GET_POST_COMMENTS_SUCCESS,
                payload: data  
            });

        } catch(err) {
            dispatch({
                type: ActionType.GET_POST_COMMENTS_FAIL,
                payload: err.message
            });
        }
    }
} 
```
There not much of a different from normal actionCreator except you need to define type of dispatch before using it `dispatch: Dispatch<Action>` 

### Create a store and connect to our app then use our actionCreator to fetch some data then dump them to the screen : 
You need to create a store.ts file in your redux dir. 
```typescript
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from './reducers/combine';

export const store = createStore(reducers, {}, applyMiddleware(thunk));
```
Because this is a simple app I will build everything in App.tsx file.Go to your index.tsx file  in your src directory to provide your store:
```typescript
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {store} from './redux/store'; 
import { Provider } from 'react-redux';

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

We also need to create a custom hook to be able to use useSelector with TypeScript, you just need to create a file called `useTypeSelector` and pass this in : 
```typescript 
import { useSelector, TypedUseSelectorHook } from 'react-redux';
import { RootState } from '../redux/reducers/combine';
export const useTypedSelector: TypedUseSelectorHook<RootState> = useSelector;
```
This is documented in React-Redux with TypeScript. That is just how it is to use useSelector so there nothing much to talk about this.  

Okay now go to App.tsx, After this file I think we are done for this tutorial : 

```typescript
import React, {useState} from 'react';
import { getComments } from './redux/actionCreators/getComment';
import { useDispatch } from 'react-redux';
import { useTypedSelector } from './hooks/useTypeSelector';

function App() {
  const dispatch = useDispatch();
  const [postId, setPostID] = useState("");
  const { comments, loading, error } = useTypedSelector((state) => state.comments);

  const onSubmitHandler = async (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();

    await dispatch(getComments(postId));
  }

  return (
      <>
      <div>
        <form onSubmit={onSubmitHandler}>
          <input type={"number"} value={postId} onChange={(e) => setPostID(e.target.value)} />
          <button type="submit"> submit </button>
        </form>
      </div>

      {
        loading ? (
          <div>Loading...</div>
        ) : (
          <ul>
            {
              comments.map((comment) => {
                return(<li key={comment.id}>{comment.body}</li>)
              })
            }
          </ul>
        )
      }
      </>
  );
}

export default App;
```

And that is it, you may notice there aren't many differents with normal React components except `event: React.FormEvent<HTMLFormElement>` and `import { useTypedSelector } from './hooks/useTypeSelector';`

This is our final result : 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2khtpwzpkly3cq3lgmyf.png)

Now this is ugly :sweat_smile: I have to admit it. But this is not a tutorial about CSS. You can always add that later and it is really trivial to focus on CSS in this tutorial. 

My point is this tutorial will help you understand how to setUp redux-store with React using TypeScript, how to create an ActionCreator and how to use this ActionCreator in your app.

Here is the source code for the app : [Here](https://github.com/Tris-909/TypeScript-Redux)

If you want to dive deeper into this topic, please visit this link :
[Here](https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components)

I hope this will help you :joy: the main reason I do this tutorial is solidating my knowledge but if it helps you in some way then I glad. Please give me a star on Github if this helps you :3 

-- peace --