Redux Toolkit and Typescript (the best way to manage application state)
Tags: react, redux, typescript, javascript
Published: 2021-03-22 07:36:02

Today we will talk about a new and in my opinion a phenomenal functionality for working with Redux, namely - **Redux Toolkit** (I really hope you understand the concept of how Redux works before you start reading this article as otherwise, some terminology may not be clear for you).

**[Redux Toolkit](https://redux-toolkit.js.org/) is an updated vision of Redux developers on how to manipulate data globally and do it easily and effortlessly.**

Let's take a step back and recall what problems the classical approach has 🤔

If don't go into detail and statistical comparison, Redux isn't liked by developers (especially beginners) because of its complexity.

First, you need to spend a lot of time understanding the ideology of Redux, then spend time creating basic things (actions, action creators, reducers and etc.). 🤯

But we are developers - we want to write code, not waste time on settings. Right? 👩‍💻

Below I will describe **my vision** of how you can work with Redux Toolkit and use it with TypeScript (since I adore TypeScript and sincerely recommend using it in your projects).

## Installation ⌛
I want to start by using the standard **create-react-app** structure with TypeScript.

This can be done with the following commands:
```
# npm
npx create-react-app my-app --template typescript

# yarn
yarn create react-app my-app --template typescript
```
After that, let's add the **toolkit** module directly to our project:
```
# npm
npm install @reduxjs/toolkit

# yarn
yarn add @reduxjs/toolkit
```

And directly the **react-redux** module:

```
# npm
npm install react-redux

# yarn
yarn add react-redux
```


## Deep dive into Redux Toolkit 🤿
The new and most important concept that we will immediately encounter will be - **slice.**

To begin within the **src** folder I will create one more folder with the name - **slices.**

Inside, I'll create a **profile.ts** file that will contain functionality that pertains to the user's profile (it's a simple division by type of work, nothing special).

![0_LzfvlubsmSG3dh7W](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lhzsayhad0wcmr2p39xp.png)


*(React is flexible in terms of architecture, so you can choose a different files location. My choice is based on the ability to conveniently use the absolute paths provided by default when using react with TypeScript. You just need to find tsconfig.json file, find "compilerOptions" object and add another field - "baseUrl": "src")*

We go to the profile.ts **profile.ts file**. In order to create a slice directly, you need to import the function that is responsible for it, namely - **createSlice**. Together with it, we import the module - **PayloadAction** (about which I will tell later).

```
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
```
The createSlice function accepts an object with main fields:
* **name** - describe the type of actions inside (for example, actions on user data or actions on authentication data);
* **the initial value for our redux state** (any data type, mainly array, or object);
* **reducers** (an object that will contain methods that will implement changes in our redux state).

```
const profileSlice = createSlice({
  name: 'profile',
  initialState: initialState,
  reducers: {},
});
```
Everything is clear with the **name**.

The next field is **initialState**. Let's create it above the implementation of the **createSlice** function (**initialState** can be made into a separate file depending on your wishes and the size of this very **initialState**).

It will look like a simple object with one field - **email**:
```
const initialState = {
  email: '',
};
```

The last field - **reducers**. Inside we describe the methods that will implement the change of our redux state.

Let's implement the **changeEmail** method (which represents just action creator).

```
const profileSlice = createSlice({
  name: 'profile',
  initialState: initialState,
  reducers: {
    changeEmail: (state, { payload }: PayloadAction<TPayload>) => {
      state.email = payload.email;
    },
  },
});
```

Now let's deal with everything that is described above and what is used here.

The first thing I want to note is that the **changeEmail** method takes two parameters **(identity how a reducer does in a classic Redux structure).**

**The first parameter - state**, which will contain the initial value and will change after applying some of our methods.

**The second parameter is the object that represents the action** (in the classic use of this object contains the type field and the payload field), but in our, we use only the payload, which is obtained by destructuring (ES6).

We add type for our payload using **PayloadAction** (which we imported). 
**PayloadAction** takes the type created above implementation of function **createSlice**:

```
type TPayload = {
  email: string;
};
```

Now let's look directly inside in our **changeEmail** method:

```
changeEmail: (state, { payload }: PayloadAction<TPayload>) => {
  state.email = payload.email;
}
```

The first thing we can see is a somewhat unconventional approach to change the Redux state. We simply assign new values ​​to **state.email** with the values ​​we get with the payload.

And what about the rule of **immutability**, which we must always remember when we change the Redux state? (*If you don't fully understand what I'm talking about, then I'll make a very quick explanation of what I mean*).

> The main concept of immutability when changing the Redux state is to always make a copy of the current Redux state (mostly using the spread operator with ES6 for objects and arrays) and then change the copy and return it as a new state of our Redux state. This is so that React can recognize the changes (since the references have changed) and redraw our component that uses data from the Redux state.

In this regard, the Redux Toolkit "under the hood" uses an additional module called **[immer](https://github.com/immerjs/immer)**. This module takes on the job of copying and granting just these rules of immutability. Therefore, we can not worry about it and change the fields directly.

Let's summarize what happened. **We just took the old value from the Redux store and replaced it with a new one**, which we passed to one of our components (will see later).

That's it, our first slice is ready, with which I congratulate you! 🎉.

After implementing **createSlice**, let's do the following:

```
export const profileReducer = profileSlice.reducer;
export const { changeEmail } = profileSlice.actions;
```

**The first export** is our reducer, which we will call **profileReducer** (we will need it soon).

**The second export** is just our created **changeEmail** method (our action creator).

All this is available to us by referring to our created slice - **profileSlice**.

## Redux store configuration 🔧

Let's use our **profileReducer** (the first export from the **profile.ts** file) and work on a full-fledged redux connection.

In the **src** folder, create another folder - **reducer**. Inside create a file **reducer.ts**:

![0_uPjWKkj9W3YRsue9](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ysuyg0zx6ci9aehnh050.png)

**reducer.ts** will look like this:
```
import { profileReducer } from "slices/profile";

const reducer = {
  profileReducer,
  // another reducers (if we have)
};
export default reducer;
```

We import our created reducer so that in the future we can add another and combine them together.

I will note one more interesting thing - the reducer object in which we can add other reducers is analogous to the use of the **[combineReducers](https://redux.js.org/api/combinereducers)** function but without the additional import of this **combineReducers** function and the generally compact appearance of the whole combination structure.

Let's create another file - **store.ts** in the **src** folder:
```
import { configureStore } from "@reduxjs/toolkit";
import reducer from "./reducer/reducer";

export const store = configureStore({ reducer });
export type TStore = ReturnType<typeof store.getState>;
```

[If we used the standard Redux approach](https://redux-toolkit.js.org/api/configureStore), it would be an ideal place to add middleware (for example thunk) and connect redux-dev-tool. But we use Redux Toolkit, and here everything is much more interesting 🤩.

This is where the magic is hidden, which we don't see 🔮. In fact, the **Redux-Toolkit already connected thunk and redux-dev-tool "under the hood"** when we just used the **configureStore** function. Only 3 lines of code and what is great the result.

Additionally, on the last line, you can see the creating of the **TStore** type. It allows us to use this type in the case when we want to grab data from the Redux store (for example using selector hook - **[useSelector](https://react-redux.js.org/api/hooks#useselector)**).

We go further and go to the last step of the redux connection - the connection in the **index.tsx** file:

```
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { store } from "store";
import App from "./App";

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

This simply uses the **Provider** component, which should by default wrap our main internal component (**App in our case**) and use the store property with the value which has name store too (which we created and in the previous step and imported here).

We finished with the setup. Now let's use what we've been working on.

## Using 🏄‍♂️

Go to the component where we want to use our previously created action creator (in my case it is the **App.tsx** file).

We import the **useDispatch** function (which we will need) and directly our action creator - **changeEmail**.

```
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { changeEmail } from "slices/profile";

const App = () => {
  const dispatch = useDispatch();
  const { email } = useSelector((state: TStore) => state.profileReducer);
  const handleEmailChange = () => {
    dispatch(changeEmail({ email: "newEmail@gmail.com" }));
  };

return (
  <div>
    <button onClick={handleEmailChange}>
      Change email
    </button>    
    <h2>
      Email: {email}
    </h2>
  </div>
);
}

export default App;
```

We added a button with an event - **onClick** which provides as add **handleEmailChange** function. Inside we call function dispatch with our action creator - **changeEmail** as a parameter. Inside **changeEmail** we see an object - it's actually our **payload object**.📦

Below the button, we have a header. Inside we use the variable - **email**. You've probably noticed that we got this variable from the Redux store using the previously mentioned hook - **useSelector**. useSelector accepts the function with the - state (to which we add the **TStore** type) parameter and returns the value we want to get.

After clicking the button we see a new result.

![1_OTrRse2rR5azAz-qz5iFKQ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c5y5ysx307xj8ljyoluu.gif)
 
If we check **[Redux Dev-Tool](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=ru)** (hope you have this useful extension) we can notice what everything is works and our **Redux store** is changed and now we have a new value for the field - **email**.

![1_CMjwzAY05u5d_UyZPu2oMQ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0gawerlem022dn0nkk5n.png)

That's it, we have full functionality when working with Redux Toolkit. 
We can create other methods, create other slices, [perform asynchronous actions](https://redux-toolkit.js.org/api/createAsyncThunk#createasyncthunk) and use it all in our application. 

Final code example you can check [here](https://codesandbox.io/s/react-toolkit-typescript-ctnox?file=/src/App.tsx:236-247).

Thank you very much for taking the time to read my article. I hope she helped you figure out the Redux Toolkit.

As always open to feedback ❤️