DEV Community

Cover image for How to Setup your React app with Redux Toolkit and Typescript
Zhitomir Oreshenski
Zhitomir Oreshenski

Posted on • Updated on

How to Setup your React app with Redux Toolkit and Typescript

React – a JavaScript library for building user interfaces --> reactjs

Typescript – is an open-source language that extends JavaScript by adding types. --> typescript

Redux is a predictable state container for JavaScript apps. It helps you write applications that behave consistently, run in different environments (client, server and native). --> redux

Redux Toolkit – as per official Redux documentation, it is recommended approach for writing Redux logic. It wraps around the Redux core, and contains all needed packages and functions for start building a Redux app. --> redux-toolkit

Why do we want to write single page apps? - The main reason is that they allow us to offer a more-native-app-like experience to the user.

Developing modern web applications involves not only UI building, but also require state management. One of the most comprehensive library for this is Redux. In this tutorial you will learn how to setup Redux using the latest libraries and simplified techniques available in 2020.

Table of Contents

  • Why to choose Redux Toolkit
  • How to Setup Create-React-App with Typescript and Redux
  • How to Structure your Redux
  • Connecting Redux with React using useDispatch and useSelector hooks
  • Summary

Why to choose Redux Toolkit

  • Simple – Includes utilities to simplify common use cases like store setup, creating reducers, immutable update logic, and more.

  • Opinionated – Provides good defaults for store setup out of the box, and includes the most commonly used Redux addons build-in.

  • Powerful – Takes inspiration from libraries like Immer and Autodux to let you write ‘mutative’ immutable update logic, and even create entire ‘slices’ of state automatically.

  • Effective – Lets you focus on the core logic your app needs, so you can do more work with less code.


How to Setup Create-React-App with Typescript and Redux

For this Redux toolkit tutorial lets start with setup a new react application using CRA:

npx create-react-app redux-toolkit –template typescript

or

yarn create-react-app redux-toolkit –template typescript

npx on the first line is no a typo – it’s a package runner tool that comes with npm 5.2+

Note:

If you’ve previously installed creat-react-app via npm globally, please uninstall the package using "npm uninstall name_of_the_package" to ensure that npx always uses the latest version. Global installs of create-react-app are no longer supported.

cd redux-toolkit

npm start or yarn start (in case you are using ‘yarn’)

Please double check if following packages are installed. In case they are not present, to add typescript to a create-react-app project, first install it:

npm install –save typescript @types/node @types/react @types/react-dom @types/jest

or

yarn add typescript @types/node @types/react @types/react-dom @types/jest

Next we will add redux-toolkit, redux-logger and uuid with following:

npm install –save react-redux redux-logger @reduxjs/toolkit uuid

or

yarn add react-redux redux-logger @reduxjs/toolkit uuid


How to Structure your Redux

src
App
    App.tsx
    App.css
type.d.ts
index.tsx
index.css
store
    todos
        index.ts
        todos.initialState.ts
        todos.reducer.ts
        todos.slice.ts
root-reducer.ts
store.ts
Enter fullscreen mode Exit fullscreen mode

Now we are ready to start configuring our store:

Step 1: Create a folder "/src/store"

All files related to our Redux will be placed here.

Step 2: Create a file "store.ts" in "src/store"

This file will contain "configureStore" function that is abstraction over the standart Redux "createStore" function and it is responsible for the store setup. If we want to use "redux-logger" and apply custom middleware, we need to import "getDefaultMiddleware()" function and spread all available props together with "logger", then pass it as a prop to "configureStore".

import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';
import logger from 'redux-logger';

import { reducer } from './root-reducer';

const middleware = [...getDefaultMiddleware(), logger];

export default configureStore({
  reducer,
  middleware,
});
Enter fullscreen mode Exit fullscreen mode

Step 3: Create a file "root-reducer.ts" in "src/store"

Because we structure the app in the long run to scale, we want to have a separate "root-reducer.ts" file, so that we can import additional reducers.

import { todosSlice } from './todos/todos.slice';

export const reducer = {
  todos: todosSlice.reducer
};

Enter fullscreen mode Exit fullscreen mode

Now we do not have a todoSlice in our reducer, lets create it:

Step 4: Create a folder "src/store/todos"

This folder will contain all the logic related to "todos slice".

Step 5: Create following files: "index.ts", "todos.initialState.ts", "todos.reducers.ts", "todos.slice.ts"

All todo's logic of the store state(interfaces, reducer, actions, todo slice)

Step 6: Lets start with "todos.slice.ts" in "src/store/todos"

import { createSlice } from '@reduxjs/toolkit';
import { todosInitialState, todosReducer } from '.';

*createSlice - a function that accepts an initial state, an object full of reducer 
functions, and a slice name that automatically generates action creators and action 
types that correspond to the reducers and state.  Internally it uses createAction and createReducer* 

export const todosSlice = createSlice({
  name: 'todos', // name used in action types
  initialState: todosInitialState, // initial state for the reducer
  reducers: todosReducer,
});

export const {
  actions: { // action creators exported from todosSlice.actions available out of the box
    create: createTodoActionCreator
  },
} = todosSlice;
Enter fullscreen mode Exit fullscreen mode

Step 7: Then proceed with our ‘"todos.initialState.ts"’ in "src/todos":

import { v1 as uuid } from 'uuid';
import { ITodo } from '../../type';

export const todosInitialState: ITodo[] = [
  {
    id: uuid(),
    desc: 'Learn Redux-ToolKit',
    isComplete: false,
  },
];
Enter fullscreen mode Exit fullscreen mode


CreateTodo.tsx:

Step 8: Lets add missing information in "src/store/todos/todos.reducer.ts"

Note:
In order to be more consistent it is advisable, for all of the models below to have respective interface. For the purpose of this tutorial some of these parts are simplified.

import { PayloadAction } from '@reduxjs/toolkit';
import { v1 as uuid } from 'uuid';

import { ITodo } from '../../type';

export const todosReducer = {
  create: {
    reducer: (
      state: ITodo[],
      action: PayloadAction<{ id: string; desc: string; isComplete: boolean }>
    ) => {
      state.push(action.payload);
    },
    prepare: ({ desc }: { desc: string }) => ({
      payload: {
        id: uuid(),
        desc,
        isComplete: false,
      },
    }),
  }
};

Enter fullscreen mode Exit fullscreen mode

Step 9: Then exports these files in "src/store/todos/index.ts"

export * from './todos.reducer';
export * from './todos.initialState';
export * from './todos.slice';
Enter fullscreen mode Exit fullscreen mode

Connecting Redux with React Components with useDispatch and useSelector hooks

Step 10: Create a folder "src/components"

All components will be placed there for now.

Step 11: Create a folder "src/app"

Step 12: Move files "App.tsx" and "App.css" into "src/app"

Step 13: Populate "App.tsx" with following peace of code:

import React from 'react';
import { useSelector } from 'react-redux';

import { State } from '../type';
import { CreateTodo, TodoList } from '../components';

import './App.css';

const App = function () {
  const todos = useSelector((state: State) => state.todos);

  return (
    <div className="App">
      <div className="App__header">
        <h1>Redux Toolkit</h1>
        <CreateTodo />
      </div>
      <div className="App__body">
        <TodoList todos={todos} />
      </div>
    </div>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

Step 14: Create following files into "src/components":

CreateTodo.tsx:

import React, { FC, FormEvent, useState, ChangeEvent } from 'react'
import { useDispatch } from 'react-redux';
import { createTodoActionCreator } from '../store/todos';

interface ICreateTodoProps { }

export const CreateTodo: FC<ICreateTodoProps> = () => {
  const dispatch = useDispatch();

  const [newTodoInput, setNewTodoInput] = useState<string>('');

  const handleCreateNewTodo = (e: FormEvent<HTMLFormElement>): void => {
    e.preventDefault();
    if (!newTodoInput.length) return;

    dispatch(createTodoActionCreator({ desc: newTodoInput }));
    setNewTodoInput('');
  };

  const handleNewInputChange = (e: ChangeEvent<HTMLInputElement>): void => {
    setNewTodoInput(e.target.value);
  };

  return (
    <form onSubmit={handleCreateNewTodo}>
      <label htmlFor="new-todo">Add new:</label>
      <input
        onChange={handleNewInputChange}
        id="new-todo"
        value={newTodoInput}
      />
      <button type="submit">Create</button>
    </form>
  )
}
Enter fullscreen mode Exit fullscreen mode

Todo.tsx:

import React, { FC } from 'react'
import { ITodo } from '../type'

interface ITodoProps {
  key: string;
  todo: ITodo
}

export const Todo: FC<ITodoProps> = ({ key, todo }) => <li>{todo.desc}</li>

Enter fullscreen mode Exit fullscreen mode

TodoList.tsx:

import React, { FC } from 'react'
import { ITodo } from '../type'
import { Todo } from '.'

interface ITodoList {
  todos: ITodo[]
}
export const TodoList: FC<ITodoList> = ({ todos }) => {
  return (
    <ul className="App__list">
      <h2>My Todos:</h2>
      {todos &&
        todos.map((todo, i: number) => (
          <Todo key={todo.id} todo={todo} />
        ))}
    </ul>
  )
}
Enter fullscreen mode Exit fullscreen mode

Step 15: Create "type.d.ts" file into root folder:

export interface ITodo {
  id: string;
  desc: string;
  isComplete: boolean;
}

export interface State {
  todos: Todo[];
}
Enter fullscreen mode Exit fullscreen mode

Summary

It’s amazing how simple is to setup Redux in 2020. We used tools Typescript, React Hooks, Redux Toolkit

Thank you for reading the article! I hope you find it useful 😊.

Oldest comments (0)