DEV Community

Gomandev
Gomandev

Posted on

No More Redux Boilerblate Hell.(Redux Toolkit)

A moment ago @yossefmohamed asked a question on how can he use Redux and Next.js. Well, developers get intimidated by redux because of its boilerplate code before you get it up and running. But they are actually a really cool tool that makes any redux user’s life easy.
Today what I will be showing you guys is Redux Toolkit with a simple CRUD example using Next.js. It doesn’t matter if you are already familiar with redux or just got started working with redux, this post is for you.

What is redux-toolkit?

Redux Toolkit is an opinionated, batteries-included toolset for efficient Redux development. It comes with the most widely used Redux addons out of the box, like Redux Thunk for async logic and Reselect for writing selector functions, so that you can use them right away without having to install them separately.

Enough for the talk, let’s write some code.

First, let’s start by scaffolding a basic Next.js Typescript Project with:

npx create-next-app redux-toolkit-example --ts

Your project root would look like this:
Alt Text

Let’s start building the UI.

Go to pages/index.js and replace the default code with this:

export default function Home() {
  return (
    <div className="conatiner">
      <div className="list-container">
        <div className="list-header">
          <h1 className="title">
            Lists<span>.</span>
          </h1>
          <div className="input-field">
            <input type="text" className="search" placeholder="Search..." />
            <button className="btn">Search</button>
          </div>
        </div>
        <div className="list-body">
          <div className="list-item">
            <div className="list-item-content">milk</div>
            <button className="list-item-footer">X</button>
          </div>
          <div className="list-item">
            <div className="list-item-content">sugar</div>
            <button className="list-item-footer">X</button>
          </div>
          <div className="list-item">
            <div className="list-item-content">coffee</div>
            <button className="list-item-footer">X</button>
          </div>
          <div className="list-item">
            <div className="list-item-content">eggs</div>
            <button className="list-item-footer">X</button>
          </div>
        </div>
      </div>
    </div>
  );
}


Enter fullscreen mode Exit fullscreen mode

Then, go to styles/global.css and replace the code with this:

html,
body {
  padding: 0;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
  background-color: #fafafa;
}

a {
  color: inherit;
  text-decoration: none;
}

* {
  box-sizing: border-box;
}

.conatiner {
  max-width: 700px;
  margin: 0 auto;
}

.list-container {
  display: flex;
  justify-self: center;
  align-self: center;
  flex-direction: column;
  width: 500px;
}

.list-header {
  width: 100%;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.list-header .title {
  font-size: 2rem;
}

.list-header .input-field input {
  margin-right: 1em;
  padding: 8px 10px;
  border-radius: 10px;
  border: #ccc solid 1px;
}

.list-header .input-field button {
  display: inline-block;
  background-color: #78f0f0;
  color: #000;
  border-radius: 10px;
  border: none;
  padding: 8px 10px;
  cursor: pointer;
}

span {
  color: #78f0f0;
}

.list-body {
  width: 100%;
  margin-top: 2em;
}

.list-item {
  display: flex;
  justify-content: space-between;
  margin-bottom: 1em;
}

.list-item button {
  background-color: #78f0f0;
  color: #000;
  border-radius: 50%;
  border: none;
  padding: 8px 10px;
  cursor: pointer;
}


Enter fullscreen mode Exit fullscreen mode

Now start the project with yarn or npm depending on the package manager you are using which in my case is npm.

npm run dev

This will start the server on localhost:3000, then open localhost:3000 in your browser and you will see this printed in the webpage:
Alt Text

We are now done building the UI.

Let's dive into redux-toolkit

Let's start by installing required packages:

npm i @reduxjs/toolkit react-redux

When that is done, create a new folder in the root directory call store, then create two files config.js and rootReducer.js in /store.

rootReducer.js

this is the root of all reducers.
insert this code in rootReducer.js:

import { combineReducers } from "@reduxjs/toolkit";
import { listSlice } from "./ducks/list";

const rootReducer = combineReducers({
  list: listSlice.reducer,
});

export default rootReducer;

Enter fullscreen mode Exit fullscreen mode

What happened here is that I imported a combineReducers() function from @reduxjs/toolkit. The combineReducers helper function turns an object whose values are different reducing functions into a single reducing function you can pass to createStore. We will be having a single reducer, so combineReducers isn't necessary. But as your app grows more complex, you'll want to split your reducing function into separate functions.
And also import listSlice which we have not create yet.

config.js

config.js is a where we configure our redux toolkit.
insert this code in config.js:

import { configureStore } from "@reduxjs/toolkit";
import rootReducer from "./rootReducer";

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

export type AppDispatch = typeof store.dispatch;
export type AppThunk = ThunkAction<void, RootState, unknown, Action>;

export default store;

Enter fullscreen mode Exit fullscreen mode

We now configure the store using the configureStore function. configureStore is a friendly abstraction over the standard Redux createStore function that adds good defaults to the store setup for a better development experience. this function will automatically configure the redux devtools extension, you can also pass optional configuration to the function. to learn more refer to the docs.

Slices

Create a directory call ducks with listSlice.js file in it.
Paste this in listSlice.js:

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

export type listState = {
  list: any[];
};

const initialState: listState = {
  list: ["egg", "milk", "sugar", "coffee"],
};

export const listSlice: any = createSlice({
  name: "list",
  initialState,
  reducers: {
    addList: (state, { payload }) => void state.list.push(payload),
    removeList: (state, { payload }) =>
      void state.list.splice(state.list.findIndex((item) => item === payload)),
  },
  extraReducers: {},
});

export const { addList, removeList } = listSlice.actions;

export const listSelector = (state: any) => state.list;

Enter fullscreen mode Exit fullscreen mode

Okay we now create our first slice using the createSlice function that performs two actions and we have our initial list state with some default items.

Back to the UI

To use redux in the UI we need to install the react-redux library.

npm i react-redux

When the installation is done, replace the default code with this in the _app.js file.

import "../styles/globals.css";

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

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

export default MyApp;


Enter fullscreen mode Exit fullscreen mode

Now that we configure redux in our UI let move to index.tsx file and Replace it with this updated code:

import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { addList, listSelector, removeList } from "../store/ducks/list";

export default function Home() {
  const [input, setInput] = useState<string>("");
  const { list } = useSelector(listSelector);
  const dispatch = useDispatch();

  const addItem = () => {
    dispatch(addList(input));
  };
  const removeItem = (value: string) => {
    dispatch(removeList(value));
  };
  return (
    <div className="conatiner">
      <div className="list-container">
        <div className="list-header">
          <h1 className="title">
            Lists<span>.</span>
          </h1>
          <div className="input-field">
            <input
              onChange={(e) => setInput(e.target.value)}
              type="text"
              className="search"
              placeholder="Add"
            />
            <button onClick={addItem} className="btn">
              Add
            </button>
          </div>
        </div>
        <div className="list-body">
          {list &&
            list.map((l: string, index: number) => (
              <div key={index} className="list-item">
                <div className="list-item-content">{l}</div>
                <button
                  onClick={() => removeItem(l)}
                  className="list-item-footer"
                >
                  X
                </button>
              </div>
            ))}
        </div>
      </div>
    </div>
  );
}


Enter fullscreen mode Exit fullscreen mode

Conclusion

Note: this is only the minor basic of redux-toolkit but they are a lot more about this library.
Thank you for reading my first tutorial on Dev.to, I hope you learn something from this :).

Discussion (5)

Collapse
yossefmohamed profile image
YossefMohamed

Great Post thanks bro 👍👏👏

Collapse
gomandev profile image
Gomandev Author

Glad you found it helpful. Bro 😀

Collapse
yossefmohamed profile image
YossefMohamed

Thanks ❤️

Collapse
ivanjeremic profile image
Ivan Jeremic • Edited on

Still to much and you have a dependency hell, react-query + recoil js is for me the cleanest solution I found. Both are built with react itself.

Collapse
gomandev profile image
Gomandev Author

Yeah, thanks for your suggestion, but this tutorial is meant for redux only, not any other library. 🙂