Learn Redux the right way: With Redux-toolkit
Tags: redux, typescript, nextjs, react
Published: 2022-02-28 10:11:57

## Introduction
**Redux!** The word which you've been hearing over and over again. What is it? Why is it needed? Maybe you've glanced at the daunting Redux docs and thought "Nah, I'll learn that some other day". Well, today is that day and I guarantee that it will be easier than expected. 

Many existing Redux tutorials are outdated, but this guide will show you the latest, recommended way to implement the framework into your app using redux-toolkit. We will go over it in 5 simple steps. It is recommended that you follow along as we learn how to set up, read and write data to Redux. The rest, you can only learn through further experimenting.

## What is Redux?
Redux is a global state management system for your entire web/react app. Imagine a state which is shared by every component which can be read and updated at any level, on any page. No callback functions.

## 1. Set-up
Lets get started. For this tutorial we be using NextJs and TypeScript. Start by initialising a project.
`npx create-next-app@latest --ts`

In the root of your project, install the dependencies **react-redux** and **@reduxjs/toolkit**.
`npm i react-redux @reduxjs/toolkit`

If you would rather just start playing around with the tutorial code now, the repository can be found [on my github](https://github.com/OZCAP/nextjs-redux-example).

## 2. Create a slice
Slices are the functions which define how a global state is managed. In a slice, we define the **initial state** and also the **reducers** which define how the data is manipulated. Create the file `src/reducers/FooReducer.tsx` containing the code, below.

```
// fooReducer.tsx

import { createSlice } from '@reduxjs/toolkit';

const initialValue = { name: "Nigel", age: 63 };

export const fooSlice = createSlice({
    name: 'foo',
    initialState: {value: initialValue},
    reducers: {
        changeAll: (state, action) => {
            state.value = action.payload;
        },
        agePlusOne: (state, action) => {
            state.value.age += 1;
        }
    }
})

export const { changeAll, agePlusOne } = fooSlice.actions;
export default fooSlice.reducer;
```
It looks like there is a lot going on, but it will become clearer. Trust me. Let's focus on what matters here.
We have an `initialValue` which defines the initial value of an object containing a 'name' and 'age' value.
Under `reducers` we have two special functions which show how the data can be manipulated. We may add as many of these reducer functions as we need.

The function `changeAll` takes in an object with new key values e.g. `{name: 'Bob', age: 44}` and replaces the current key values.

The function `getOlder` takes no parameters, and increases the `age` value by 1.

## 3. Set up Provider
In order for the Redux state to be synchronised across the app, we must nest everything inside a `<Provider/>` component. Copy the code below into `pages/_app.tsx`.

```
// _app.tsx
import type { AppProps } from 'next/app'

import { configureStore } from '@reduxjs/toolkit';
import { Provider } from 'react-redux';
import fooReducer from '../src/reducers/FooReducer'

const store = configureStore({
  reducer:  {
    foo: fooReducer,
  }
});

function MyApp({ Component, pageProps }: AppProps) {
  return (
  <Provider store={store}>
    <Component {...pageProps} />
  </Provider>
  )
}

export default MyApp
```


If you are familiar with NextJs, you will know that `_app.tsx` is the root component of the application. Any page loaded from `/pages` is rendered inside `<Component {...pageProps} />` which means that all routes will always be within the `<Provider/>` component, allowing access to the global state(s) defined by `store` on any page.

## 4. Reading the global state
Go ahead and copy the following code inside your `pages/index.tsx`

```
// index.tsx
import type { NextPage } from 'next'
import { useSelector } from 'react-redux';

const Home: NextPage = () => {
  const foo = useSelector(state => state.foo.value);

  return (
    <main>
      <p>{foo.name}</p>
      <p>{foo.age}</p>
    </main>
  )
}

export default Home
```
When accessing the dev environment, we are now greeted with the text 'Nigel' and '63'. This is the initial state of the object we defined in `FooReducer.tsx`!

The `useSelector()` function this global state from the `store` we set up in our `_app.tsx`.

## 5. Writing to the global state
Edit your index.tsx and add the following code.

```
// index.tsx
//...
import { useDispatch } from 'react-redux';
import { agePlusOne } from '../src/reducers/FooReducer';

const Home: NextPage = () => {
  //...
  const dispatch = useDispatch();

  return (
    <main>
      {foo.name}
      <button onClick={() => {
        dispatch(agePlusOne(null));
      }}>Plus One</button>
      <br />
      {foo.age}
    </main>
  )
}
```
`useDispatch()` is the function which is used to execute the functions outlined in our `FooReducer.tsx`. Here, we have imported the `agePlusOne` function which adds 1 to the current age value. When we click the button, the age will increase by 1. The function takes no arguments.

If we want to do the same with the `changeAll` function, we must import it like we did with the `agePlusOne` function and call it with an argument of the new state that we want e.g. `dispatch(changeAll({name: 'Bob', age: 44}))`.

## Wrapping up
If you want to add more reducers, all you need to do is simply create additional components, for example `BarReducer.tsx` and then include it in the `store` constant defined in `_app.tsx` as below.

```
const store = configureStore({
  reducer:  {
    foo: fooReducer,
    bar: barReducer
  }
})
```
After doing this, you can then reference `bar`, as you did with `foo` and have multiple global states!