How To Use Redux with Hooks in a React-TypeScript Project
Tags: react, redux, typescript, hooks
Published: 2020-09-18 11:57:46

### Introduction
[Redux](https://redux.js.org/) is a predictable state container for JavaScript applications. In this tutorial, I will show you how to use redux to manage state in [React](https://reactjs.org/) with [TyepeScript](https://www.typescriptlang.org/) and [Hooks](https://reactjs.org/docs/hooks-intro.html).

### Getting Started 
If you are only interested in viewing complete code on GitHub, click [here](https://github.com/geraldmaboshe/posts-redux-tutorial). Otherwise, let's setup the project using [Create React App](https://github.com/facebook/create-react-app). In this tutorial I will be using yarn but you should be fine with npm as well. In your terminal run the following command
```
npx create-react-app posts --typescript
```
This command creates a React Typescript project called posts. To start the development server and view the project in your browser, run the following commands.
```
cd posts
yarn start
```
### Installations
To use redux: 
```
yarn add @reduxjs/toolkit
```
To use Redux with React and TypeScript:
```
yarn add react-redux
yarn add @types/react-redux
```
To add redux thunk:
```
yarn add redux-thunk
```
To add redux devtools:
```
yarn add redux-devtools-extension
```
### Redux
Setup your redux folder as follows
```
src
-redux
--actions
--effects
--interfaces
--reducers
--store
--types
```
The interfaces folder is used for adding all interfaces that can be used across the project. For this tutorial, we will use posts fake data from [JSONPlaceholder](https://jsonplaceholder.typicode.com/). In interfaces directory, create a file called *Post.ts* and add the following code.
```
export interface Post {
  userId: number;
  id: number;
  title: string;
  body: string;
}
```
The interface above defines a single [post] (https://jsonplaceholder.typicode.com/posts).

Now we need to setup our types. In the types folder, create a file called *PostTypes.ts* and add the following code
```
import { Post } from '../interfaces/Post';

export const GET_POSTS = 'GET_POSTS';

export interface GetPostsStateType {
  posts: Post[];
}

interface GetPostsActionType {
  type: typeof GET_POSTS;
  payload: Post[];
}
export type PostActionTypes = GetPostsActionType;
```
*GetPostsStateType* interface is defining what the state will look like; an array of posts. *GetPostsActionType* interface is defining the action type that you will see later in this tutorial.

In the reducers directory, create a file called *PostReducer.ts* and add the following code
```
import {
  GET_POSTS,
  GetPostsStateType,
  PostActionTypes
} from '../types/PostTypes';

const initialStateGetPosts: GetPostsStateType = {
  posts: []
};

export const getPostsReducer = (
  state = initialStateGetPosts,
  action: PostActionTypes
): GetPostsStateType => {
  switch (action.type) {
    case GET_POSTS:
      return {
        ...state,
        posts: action.payload
      };
    default:
      return state;
  }
};

```
In here, we initialize state of type *GetPostsStateType* that we defined earlier. We then create a reducer function called *getPostsReducer*. A reducer takes two parameters; state and action. In our case, state and action are of types *initialStateGetPosts* and *PostActionTypes* respectively while the reducer function returns *GetPostsStateType*. In the switch block, if the case is *GET_POSTS*, we return whatever is there in the state and update it with the new payload and the default case is state. Note that in a bigger project there would be a lot of cases.

Create another file in the reducers folder and lets call it *index.ts*. In here, we will combine all our reducers using *combineReducers* and export them as *rootReducer*[You can call it anything really] as shown below.
```
import { combineReducers } from 'redux';
import { getPostsReducer } from './PostReducer';

const rootReducer = combineReducers({
  posts: getPostsReducer
});

export default rootReducer;
```
Now we will create our store. A store holds the whole state tree of the application. In the store folder, let's have *index.ts* and add the following code:
```
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import rootReducer from '../reducers';
import { composeWithDevTools } from 'redux-devtools-extension';

const store = createStore(
  rootReducer,
  composeWithDevTools(applyMiddleware(thunkMiddleware))
);

export type AppState = ReturnType<typeof rootReducer>;
export default store;
```
All we are doing in here is creating a store from the combined reducers called *rootReducer*. *composeWithDevTools* will allow you to monitor global state in your browser if you've installed the [Redux Devtools Extension](https://github.com/reduxjs/redux-devtools). *applyMiddleware(thunkMiddleware)* allows us to dispatch [async actions](https://redux.js.org/glossary#async-action)

To make the store available to React components, in src/index.ts, we wrap *App* in *Provider* and pass the store as shown below
```
import { Provider } from 'react-redux';
import store from './redux/store';

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);
```
At this point, you should see your global state with an empty array of posts as shown below.

![Initial State](https://dev-to-uploads.s3.amazonaws.com/i/bjwxmopoojd9lcc3i4ek.PNG)

The only way to change the state in the store is through an action dispatch. In the actions folder create *PostActions.ts* and add the following code:
```
import { GET_POSTS, PostActionTypes } from '../types/PostTypes';
import { Post } from '../interfaces/Post';

export const getPostsAction = (posts: Post[]): PostActionTypes => {
  return {
    type: GET_POSTS,
    payload: posts
  };
};
```
The *getPostsAction* function accepts an array of posts and returns a type of *GET_POSTS* and posts data passed to the payload variable. Note that type and payload can be given names of your choice.

To fetch our posts from the fake API, let's create *Posts.ts* in the effects folder and add the following code.
```
import { getPostsAction } from '../actions/PostActions';
import { Dispatch } from 'redux';
import { PostActionTypes } from '../types/PostTypes';
export const getPosts = () => {
  return function (dispatch: Dispatch<PostActionTypes>) {
    const POST_URL = 'https://jsonplaceholder.typicode.com/posts';
    fetch(POST_URL, {
      method: 'GET'
    })
      .then(res => res.json())
      .then(data => {
        dispatch(getPostsAction(data));
        return data;
      });
  };
};

```
All we are doing here is dispatching the *getPostsAction* and passing it the data from the fake API. 

### React Component
Finally, in *App.ts*, we can access our App State. Update *App.ts* as follows:
```
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { getPosts } from './redux/effects/Posts';
import { Post } from './redux/interfaces/Post';
import { AppState } from './redux/store';

export default function Posts() {
  const dispatch = useDispatch();
  useEffect(() => {
    dispatch(getPosts());
  }, [dispatch]);
  const posts = useSelector((state: AppState) => state.posts);
  const postItems = posts.posts.map((post: Post) => (
    <div key={post.id}>
      <h1>{post.title}</h1>
      <p>{post.body}</p>
    </div>
  ));
  return <div>{postItems}</div>;
}

```
In here, we bring in *useDispatch* and *useSelector* from *react-redux*. The *useDispatch* hook is used to dispatch actions as needed. In our case, we are passing the *getPosts* effect to dispatch in the *useEffect* hook. This will add the data coming from the fake API to our redux store as soon as the App component mounts. At this point your redux store should look like this:

![Posts State](https://dev-to-uploads.s3.amazonaws.com/i/npo0akc1d15g1s1eh5ar.PNG)

*useSelector* works more or less like [mapStateToProps](https://react-redux.js.org/using-react-redux/connect-mapstate) when using [connect](https://react-redux.js.org/api/connect). It allows us to access app state in a React functional component. In our case we are interested in getting posts from the posts state and that is exactly why we are iterating through *posts.posts*. Then we display the post title with *post.title* and body with *post.body*. Interesting right?

### Conclusion
There are many ways you could use redux in your React project. Go with a setup that works for you. Redux can have a lot of boilerplate but comes in handy once the boilerplate code is out of the way.

Happy coding!