# Build a Fabulous Todo List App with React, Redux and Framer-Motion

> Learn how to build a fabulous todo list app using React, Redux, and Framer-Motion. This guide will walk you through implementing CRUD operations and adding awesome animations to your application.

- **Source:** DevDreaming (https://devdreaming.com)
- **Canonical URL:** https://devdreaming.com/blogs/build-react-redux-framer-motion-todo-app
- **Author:** CodeBucks
- **Published:** 2021-04-21
- **Last updated:** 2023-01-27
- **Topics:** React js, Redux

---

Hi there,

I know building TODO List always won't get you too far😴, But It can teach you basic concepts and implementation of particular framework. Here is the demo of what we're going to build in this tutorial.👇

**Demo Link:** [Awesome React Redux Todo List App](https://react-redux-todo-app-lac.vercel.app/)

> **Remember**
>
> Now if you're starting this tutorial watch till the end because doing it half won't teach you anything. So let's dig in.🤩

What will you learn from this tutorial?

- How to use [Redux Toolkit](/blogs/category/redux)
- How to use Framer-Motion for awesome animations
- Method to sort and display lists
- CRUD operations (obviously🤭)

If you prefer to code along with the same steps while listing to music you can watch this video 👀:

[▶ Watch the video on YouTube](https://www.youtube.com/watch?v=YhgSuUkWlK4)

> You must have basic understanding of Redux to follow this tutorial, don't worry if you don't know the basics of Redux you can visit my channel, there is playlist to
>
> [learn redux](https://youtube.com/playlist?list=PLjxZxD6BDkeYHNuoy6nX2KS4468C2neHS)
>
> .

### Setting Up The Project And Installing Dependencies

Here is the folder structure that we are going to use for this small project so make sure to create it.

> **Folder Structure**
>
> In the `src` folder,
>
> |--redux(folder) \
>
>     |--reducer.js (here we will create actions and reducer)\
>
>     |--store.js \
>
> |--components(folder) \
>
>     |--Todos.js \
>
>     |--TodoItem.js \
>
>     |--DisplayTodos.js \
>
> |--css(folder) \
>
>     |--main.css

Write following commands to create react-app and install required libraries!

```shell
npx create-react-app your-app-name
cd your-app-name
npm install react-redux @reduxjs/toolkit framer-motion react-icons
```

We're going to use `react-icons` to add svg files in our app. Let's add one input and add button in the `Todos.js` .

```javascript
import React, { useState } from "react";

const Todos = (props) => {
  const [todo, setTodo] = useState("");
  const handleChange = (e) => {
    setTodo(e.target.value);
  };

  return (
    <div className="addTodos">
      <input
        type="text"
        onChange={(e) => handleChange(e)}
        className="todo-input"
        value={todo}
      />
      <button className="add-btn">
        Add
      </button>
      <br />
    </div>
  );
};
export default Todos;
```

As you can see in above code it has one input with `handleChange()` method and one add `button`.

### Creating a Reducer And Store

Now let's create Reducer and actions for this application. Open `reducer.js` file and write the following code:

```javascript
import { createSlice } from "@reduxjs/toolkit";
const initialState = [];
const addTodoReducer = createSlice({
  name: "todos",
  initialState,
  reducers: {
    //here we will write our reducer
    //Adding todos
    addTodos: (state, action) => {
      state.push(action.payload);
      return state;
    },
  },
});

export const { addTodos } = addTodoReducer.actions;
export const reducer = addTodoReducer.reducer;
```

Now here we're going to use [createSlice()](https://redux-toolkit.js.org/api/createSlice) function. This function takes 1 object having 3 parameters,

- name of the slice function
- initial State
- All reducer logic inside reducers `{}` object

**Line 1:** Importing the `createSlice` function.

**Line 2:** creating initial state, it is an empty array.

**Line 5:** Here we have used `createSlice` function and passed all 3 required parametrs.

**Line 13:** We are creating one action called `addTodos` this action get an callback function which have two arguments ( `state` , `action` ). Then this function will return state with adding `action.payload` (payload contains one todo item).

**Line 22:** Here we are exporting `addTodos` as action from `addTodoReducer` .

**Line 23:** In this line we are exporting `reducer` from `addTodoReducer` .

#### Creating a Store

Now, let's create store and pass this reducer. Open `store.js` and write the following code:

```javascript
import { configureStore } from "@reduxjs/toolkit";
import { reducer } from "./reducer";
const store = configureStore({
  reducer: reducer,
});
export default store;
```

In the above code we have used [configureStore](https://redux-toolkit.js.org/api/configureStore) function which takes one `reducer` and automatically takes care for the Redux DevTools extension so we don't have to worry about it. Now the `store` is ready with `reducer` that we have created.

#### Connecting Redux Store With React App

Let's connect this store to our React application. I like to connect `store` in the `index.js` file. Open the `index.js` file and import `Provider` from the `react-redux` and `store` from `store.js` file.

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

Now just wrap your `<App/>` component with this `Provider` and pass `store` in the `Provider` as the following code block,

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

Now our Redux store is connected with our React App.

#### Connect React component with Redux

Let's use this store and [Redux functionalities](/blogs/redux-simply-explained) in the `Todos.js` component. To connect this component with Redux we will use [connect()](https://react-redux.js.org/api/connect) method from `react-redux` . Open `Todos.js` file and import `connect` method from `react-redux` .

```javascript
import { connect } from "react-redux";
```

Now instead of simple `export default Todos` change it to the following line:

```javascript
export default connect(null, null)(Todos);
```

This is how we use `connect` method, It's like [higher order function](/blogs/higher-order-component-hoc-react) that takes your component (Todos in our case) and add redux functionalities to it then return it.

> Now add props in your component and log this props. You will see an Object having
>
> `dispatch`
>
>  method. Which means your component is now connected with Redux.

Let's use todos state in our component. To use state from Redux we have to pass `mapStateToProps` method in the `connect` method and to use actions or functions that we created inside the reducer (like `addTodos` ) we have to create and pass `mapDispatchToProps` method and add it to the coonect method. So let's create both of this methods in the `Todos` component.

```javascript
const mapStateToProps = (state) => {
  return {
    todos: state,
  };
};
```

This method takes state as argument and will return state as we want. Here, I want state as `todos` .

```javascript
const mapDispatchToProps = (dispatch) => {
  return {
    addTodo: (obj) => dispatch(addTodos(obj)),
  };
};
```

This method takes `dispatch` as argument and it can dispatch action to `reducer` . Here, I want to add todos so this method returns an `addTodo` method. The `addTodo` method dispatch an `addTodos` action with an obj (which contains todo item, it will acts as action.payload).

> Here, make sure to import
>
> `addTodos`
>
>  action from
>
> `reducer`
>
>  file.

Now add both of these methods in the connect just like the following code block,

```javascript
export default connect(mapStateToProps, mapDispatchToProps)(Todos);
```

let's connect input and add button with this state and methods.

```javascript
import React, { useState } from "react";
import { connect } from "react-redux";
import { addTodos } from "../redux/reducer";

const mapStateToProps = (state) => {
  return {

    todos: state,

  };
};

const mapDispatchToProps = (dispatch) => {
  return {

    addTodo: (obj) => dispatch(addTodos(obj)),

  };
};

const Todos = (props) => {

  console.log("props", props);

  const [todo, setTodo] = useState("");

    const add = () => {
    if (todo === "") {
      alert("Input is Empty");
    } else {
      props.addTodo({
        id: Math.floor(Math.random() * 1000),
        item: todo,
        completed: false,
      });
      setTodo("");
    }

  };

  const handleChange = (e) => {

    setTodo(e.target.value);

  };

  return (

    <div className="addTodos">
      <input
        type="text"
        onChange={(e) => handleChange(e)}
        className="todo-input"
        value={todo}
      />

      <button className="add-btn"  onClick={() => add()}>
        Add
      </button>
      <br />

      <ul>
        {props.todos.length > 0 &&
          props.todos.map((item) => {
            return <li key={item.id}>{item.item}</li>;
          })}
      </ul>

    </div>

  );
};

//we can use connect method to connect this component with redux store
export default connect(mapStateToProps, mapDispatchToProps)(Todos);
```

**Line 23:** Here I have created `add` function. First it will check if todo state is not empty if it is empty then shows an alert else it will use `addTodo` method from props. In this method we will pass todo object which contains `id`, `todo` text, `completed` boolean which is initially false.

**Line 50:** Make sure to connect `add()` with `onClick` of button.

**Line 55:** here I have mapped values from `todos` state. If `todos.length > 0` then it will map it and shows all todo items you add.

> You can also use Redux DevTools Extension to see actions and state.

#### Adding All Operations in The Reducer

Let's add all the required operations in the `reducer`.

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

const initialState = [];

const addTodoReducer = createSlice({
  name: "todos",
  initialState,
  reducers: {
    //here we will write our reducer
    //Adding todos
    addTodos: (state, action) => {
      state.push(action.payload);
      return state;
    },
    //remove todos
    removeTodos: (state, action) => {
      return state.filter((item) => item.id !== action.payload);
    },
    //update todos
    updateTodos: (state, action) => {
      return state.map((todo) => {
        if (todo.id === action.payload.id) {
          return {
            ...todo,
            item: action.payload.item,
          };
        }
        return todo;
      });
    },
    //completed
    completeTodos: (state, action) => {
      return state.map((todo) => {
        if (todo.id === action.payload) {
          return {
            ...todo,
            completed: true,
          };
        }
        return todo;
      });
    },
  },
});

export const {
  addTodos,
  removeTodos,
  updateTodos,
  completeTodos,
} = addTodoReducer.actions;
export const reducer = addTodoReducer.reducer;
```

**Line 16:** Here, the `removeTodos` will filterout items whose id is same as `action.payload` . (which means while using this action we will pass id as payload)

**Line 20:** The `updateTodos` is used to change todo text or `todo.item` . It will check if id is same as passed in `action.payload` then it will return all other properties of the item and change the text of todos with the passed value.

**Line 32:** The `completeTodos` will change the completed boolean value of particular item to true.

**Line 46:** Make sure to export all the required todo actions.

Now we will use all these actions. Let's separate display todos component from `Todos.js` file. Remove `ul` list from it and let's add it in the `DisplayTodo` item component. Before creating `DisplayTodos.js` component, first let's create `TodoItem.js` component. so, open `TodoItem.js` file and write the following code.

> Don't read this code, First read the explanation.

```javascript
import React, { useRef } from "react";
import { AiFillEdit } from "react-icons/ai";
import { IoCheckmarkDoneSharp, IoClose } from "react-icons/io5";

const TodoItem = (props) => {

  const { item, updateTodo, removeTodo, completeTodo } = props;

  const inputRef = useRef(true);

  const changeFocus = () => {

    inputRef.current.disabled = false;
    inputRef.current.focus();

  };

  const update = (id, value, e) => {

    if (e.which === 13) {
      //here 13 is key code for enter key
      updateTodo({ id, item: value });
      inputRef.current.disabled = true;
    }

  };
  return (

    <li
      key={item.id}
      className="card"
    >
      <textarea
        ref={inputRef}
        disabled={inputRef}
        defaultValue={item.item}
        onKeyPress={(e) => update(item.id, inputRef.current.value, e)}
      />
      <div className="btns">
        <button  onClick={() => changeFocus()}>
          <AiFillEdit />
        </button>
        {item.completed === false && (
          <button
            style={{ color: "green" }}
            onClick={() => completeTodo(item.id)}
          >
            <IoCheckmarkDoneSharp />
          </button>
        )}
        <button
          style={{ color: "red" }}
          onClick={() => removeTodo(item.id)} >

          <IoClose />
        </button>

      </div>
      {item.completed && <span className="completed">done</span>}

    </li>

  );
};

export default TodoItem;
```

Now as you saw in the demo each todo item contains 3 buttons _edit_,_completed_,_delete_. and 3 methods connected with these buttons.

**Line 2 & 3:** This contains import of icons from `react-icons` library, we will use this icons in edit, update and remove buttons.

**Line 7:** These are the all required items that we have to pass while displaying `TodoItem` component.

- `item`: contains all the data of single todo item.
- `updateTodo`: Method to update todo.
- `completeTodo`: method to set todo is completed.
- `removeTodo`: method to remove todo item.

**Line 23:** Here in the return inside this `li` element you can see,

- `textarea`: it shows the todo text as default value.
- `buttons`: after text area there are 3 buttons which contains icons for edit, update and remove, this buttons are connected with all required methods.
- `span`: after these buttons there is one span element which shows done, and it will display only when `item.completed`is true.

**Line 9:** It is a ref which is connected with `textarea`. It's value is true.

**Line 30:** Here, we have used `ref` value for the disabled attribute, which means while ref is true until then `textarea` stays disabled.

**Line 11:** This `change` function will enable the `textarea` and adds focus on it. This function is connected with the _edit_ button.

**Line 16:** This function is used to update value of the todo item. It will take 3 arguments, _id_, _updated value_ and _event_. Then when you press the enter key then it will call the `updateTodo` method and pass all required things as object and disable the textarea. It is connected on `onKeyPress` in the textarea at **Line 32**.

**Line 48:** This remove button is connected with the `remove` method. we have to pass id of the item we want to remove in this method.

Now let's use this component inside the `DisplayTodos.js` file. Open `DisplayTodos.js` and write the following code.

```javascript
import React, { useState } from "react";
import { connect } from "react-redux";
import {
  addTodos,
  completeTodos,
  removeTodos,
  updateTodos,
} from "../redux/reducer";
import TodoItem from "./TodoItem";

const mapStateToProps = (state) => {
  return {
    todos: state,
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    addTodo: (obj) => dispatch(addTodos(obj)),
    removeTodo: (id) => dispatch(removeTodos(id)),
    updateTodo: (obj) => dispatch(updateTodos(obj)),
    completeTodo: (id) => dispatch(completeTodos(id)),
  };
};

const DisplayTodos = (props) => {
  const [sort, setSort] = useState("active");
  return (
    <div className="displaytodos">
      <div className="buttons">

        <button
          onClick={() => setSort("active")}
        >
          Active
        </button>

        <button
          onClick={() => setSort("completed")}
        >
          Completed
        </button>

        <button
          onClick={() => setSort("all")}
        >
          All
        </button>

      </div>

      <ul>

          {props.todos.length > 0 && sort === "active"
            ? props.todos.map((item) => {
                return (
                  item.completed === false && (

                    <TodoItem
                      key={item.id}
                      item={item}
                      removeTodo={props.removeTodo}
                      updateTodo={props.updateTodo}
                      completeTodo={props.completeTodo}
                    />

                  )
                );
              })
            : null}
          {/* for completed items */}
          {props.todos.length > 0 && sort === "completed"
            ? props.todos.map((item) => {
                return (
                  item.completed === true && (
                    <TodoItem
                      key={item.id}
                      item={item}
                      removeTodo={props.removeTodo}
                      updateTodo={props.updateTodo}
                      completeTodo={props.completeTodo}
                    />
                  )
                );
              })
            : null}
          {/* for all items */}
          {props.todos.length > 0 && sort === "all"
            ? props.todos.map((item) => {
                return (
                  <TodoItem
                    key={item.id}
                    item={item}
                    removeTodo={props.removeTodo}
                    updateTodo={props.updateTodo}
                    completeTodo={props.completeTodo}
                  />
                );
              })
            : null}

      </ul>
    </div>
  );
};

export default connect(mapStateToProps, mapDispatchToProps)(DisplayTodos);
```

> Make sure to import
>
> `DisplayTodos.js`
>
>  component in the
>
> `App.js`
>
>  file right after the
>
> `Todos`
>
>  component.

**Line 1-9:** Contains all the requried imports.

**Line 12 & 18:** we have already discussed about both of these method. Both of these methods must be passed in the connect method. One of them is to map state to props while the other method contains all the required methods to dispatch particular actions.

**Line 28:** This state is for those 3 buttons which are active, completed and all. It is initialised with **active**.

**Line 31:** This div contains all 3 buttons and `onClick` of these buttons `sort` state gets changed based on the button it's values can be **active**, **completed** or **all**.

**Line 53:** In this `ul` element we're rendering 3 different lists based on conditions like,

- Renders active todo items when (`item.completed` === `false`) and (`sort` === "active")
- Renders completed todo items when (`item.completed` === true) and (`sort` === "completed")
- Renders all todo items when (`sort` === "all")

**Line 61-65:** This contains all the data that we need to pass in the `TodoItem` component.

> **Note**
>
> For the Framer-motion and CSS part you can watch the video or read the code from github repository. (It will be more easier to understand when you watch the video then writing here about css and animations )

Full Code of this Tutorial👇

[Build Awesome Todo App using React Redux](https://github.com/codebucks27/React-Redux-Todo-App)

---

## Related on DevDreaming

- [All Blog Posts](https://devdreaming.com/blogs)
- [Free Developer Tools](https://devdreaming.com/tools)
- [Video Tutorials](https://devdreaming.com/videos)
- [AI Tools for Developers](https://devdreaming.com/ai-tools)

---

_This is the Markdown twin of a page on **DevDreaming** -- free developer tutorials, tools, and AI resources. Source of truth: the canonical HTML URL above._