MERN stack TODO application [Frontend]
Tags: webdev, react, beginners, javascript
Published: 2021-07-21 12:55:30

We'll be creating the frontend application for our backend application.

## Before we get started

### Read Part-1: Creating the backend

In part-1, we

- Initialized backend using `npm` and installed necessary dependencies
- Set up `MongoDB` database
- Set up a server using `node.js` and `express.js`
- Created database `schema` to define a `TODO`
- Created api routes to `create`, `read`, `update` and `delete` todo

### In this part, we will

- Set up our frontend using `create-react-app`
- Create components for reading all the todo, creating todo and updating todo

### Before getting started with `Part-2`

- Store the contents of the part-1 in a folder named server and create a folder for client

The folder structure will look something like this

```
.
└── mern-todo
    ├── server
    └── client
```

## Part-2: Creating Frontend

### 1. Initializing our project

We'll initialize the `create-react-app` in the `client` folder. Run the following command from the terminal but make sure you are in the `client` folder.

```bash
npx create-react-app .
```

The `.` in the above command refers to the `current folder`. This will install our react app in the current folder instead of installing the app in a different folder.

### 2. Installing required dependencies

Within the client folder install the following dependencies

```bash
npm i node-sass axios react-router-dom
```

`node-sass`: allows using `sass` instead of `css`
`axios`: to make api calls to the `backend`
`react-router-dom`: for routing between pages

`client`'s folders `package.json` should look something like this.

```json
{
  "name": "client",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "axios": "^0.21.1",
    "node-sass": "^6.0.1",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-router-dom": "^5.2.0",
    "react-scripts": "4.0.3",
    "web-vitals": "^1.0.1"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": ["react-app", "react-app/jest"]
  },
  "browserslist": {
    "production": [">0.2%", "not dead", "not op_mini all"],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}
```

### 3. Cleaning the `src` folder

- Delete the `logo.svg`
- Remove the imports from `App.js`
- Remove the following from `App.js`

```html
<header className="App-header">
  <img src="{logo}" className="App-logo" alt="logo" />
  <p>Edit <code>src/App.js</code> and save to reload.</p>
  <a
    className="App-link"
    href="https://reactjs.org"
    target="_blank"
    rel="noopener noreferrer"
  >
    Learn React
  </a>
</header>
```

and put the following placeholder. We will put the actual code later.

```html
<header>Hello, World!</header>
```

- Delete the `index.css` file and remove the corresponding import from `index.js`

- Rename the `App.css` file to `App.scss` and change the corresponding import at `App.js`

  ```js
  import "./App.scss" //updated
  ```

- Run `npm start`. Open `http://localhost:3000` and it should display `Hello, World!`

- Copy and paste the styles from [here](https://github.com/mritunjaysaha/todo/blob/master/client/src/App.scss) and paste it in the `App.scss` file.

Now, we are good to start creating the frontend application.

### 4. Creating the `components`

```
.
├── node_modules
├── public
├── src <---------- we are here
│   ├── App.js
│   ├── App.scss
│   ├── App.test.js
│   ├── index.js
│   ├── reportWebVitals.js
│   └── setupTests.js
├── .gitignore
├── package-lock.json
├── package.json
├── README.md
└── yarn.lock
```

Create a `components` folder inside the `src` folder and add the following files

- `createTodo.jsx`
- `showTodoList.jsx`
- `updateTodo.jsx`

After adding these files, the folder structure will look something like this

```
.
├── node_modules
├── public
├── src
│   ├── components
│   │   ├── createTodo.jsx
│   │   ├── showTodoList.jsx
│   │   └── updateTodo.jsx
│   ├── App.js
│   ├── App.scss
│   ├── App.test.js
│   ├── index.js
│   ├── reportWebVitals.js
│   └── setupTests.js
├── .gitignore
├── package-lock.json
├── package.json
├── README.md
└── yarn.lock
```

#### i. `READ` all the todo

```
.
├── node_modules
├── public
├── src
│   ├── components
│   │   ├── createTodo.jsx
│   │   ├── showTodoList.jsx <-- we are here
│   │   └── updateTodo.jsx
│   ├── App.js
│   ├── App.scss
│   ├── App.test.js
│   ├── index.js
│   ├── reportWebVitals.js
│   └── setupTests.js
├── .gitignore
├── package-lock.json
├── package.json
├── README.md
└── yarn.lock
```

First, we will create the `ShowTodoList` component, to read all the documents that we created in the previous part while testing the backend application.

- Import `useState` and `useEffect` hooks from `react`
- Import `axios` from `axios`

In `ShowTodoList` function component will have a state `todo`, we will fetch the documents from the database and store it in the state `todo`.

We will use `axios` to send a `GET` request to the backend to fetch the document. Upon receiving the data we will store the data in `todo` using `setTodo` and log the data. If we receive an error we'll log that too.

We will make the get request from the `useEffect` hook, since we want the data to load when the page loads.

We will use the `TodoCard` component to display the contents of the `todo`. We will use `map` to iterate over `todo` and pass the contents to `TodoCard` which will display the contents of each todo document.

The contents of the `showTodoList.jsx` file should look something like this

{% gist https://gist.github.com/mritunjaysaha/5d264809e9a67c6ff7ea353466d830fe %}

We will import `ShowTodoList` component in the `App.js` file

The contents of the `App.js` file should look something like this

{% gist https://gist.github.com/mritunjaysaha/57ac2179eeaf437cf4538db05361ff67 %}

Now, start the `server` that we built in `part-1`

```bash
npm run dev
```

Now, start the `client` side application

```bash
npm start
```

Open `http://localhost:3000` in your browser and it should display all the todo documents that was fetched from the database.

#### ii. `CREATE` a new todo

```
.
├── node_modules
├── public
├── src
│   ├── components
│   │   ├── createTodo.jsx <-- we are here
│   │   ├── showTodoList.jsx
│   │   └── updateTodo.jsx
│   ├── App.js
│   ├── App.scss
│   ├── App.test.js
│   ├── index.js
│   ├── reportWebVitals.js
│   └── setupTests.js
├── .gitignore
├── package-lock.json
├── package.json
├── README.md
└── yarn.lock
```

To create a new document we will send a `POST` request to our `server` using axios.

- Import `useState` hook `react`
- Import `Link` from `react-router-dom`
- Define a function `handleChange` that will get the input data
- Define a function `handleSubmit` that will send the `POST` request to the `server`
- Declare `data` using `useState` hook with the following json

```json
{
  "title": "",
  "description": ""
}
```

In `handleChange` method we will update the `data` when the input changes. We will call the `setData()` and declare a arrow function inside that will copy the contents of the previous data if any exists. In this `e.target.name` will be the name of the input element that will have either `title` or `description`.

In `handleSubmit` method,

- Call `e.preventDefault()` to prevent the page from reloading when the submit button is clicked.
- Send a `POST` request to the server with the data. If the data has been sent successfully to the server then reset the state `data`

After adding the above change the code will look something like this

{% gist https://gist.github.com/mritunjaysaha/295e3387a020c67da8e7891ede61a46e %}

#### iii. Update `App.js`

```
.
├── node_modules
├── public
├── src
│   ├── components
│   │   ├── createTodo.jsx
│   │   ├── showTodoList.jsx
│   │   └── updateTodo.jsx
│   ├── App.js <-------------- we are here
│   ├── App.scss
│   ├── App.test.js
│   ├── index.js
│   ├── reportWebVitals.js
│   └── setupTests.js
├── .gitignore
├── package-lock.json
├── package.json
├── README.md
└── yarn.lock
```

Before we can use the `CreateTodo` component we need to update `App.js` file.

- Import `BrowserRouter` and `Route` from `react-router-dom`
- Import `CreateTodo` component from `components/createTodo`
- Create a `Route` for home page `/` and pass the `ShowTodoList` component
- Create a `Route` for creating a new todo `/create-todo`
- Wrap the `Route`s inside of the `BrowserRouter`

After making the changes the `App.js` file should look something like this

{% gist https://gist.github.com/mritunjaysaha/4c5408215a037c62a468598dd9b48ce6 %}

Since we have not added the button to navigate to `http://localhost:3000/create-todo` you can type this in your browser to check the `CreateTodo` component.

#### iv. Adding the Link to navigate to `/create-todo` to `showTodoList.jsx`

```
.
├── node_modules
├── public
├── src
│   ├── components
│   │   ├── createTodo.jsx
│   │   ├── showTodoList.jsx <-- we are here
│   │   └── updateTodo.jsx
│   ├── App.js
│   ├── App.scss
│   ├── App.test.js
│   ├── index.js
│   ├── reportWebVitals.js
│   └── setupTests.js
├── .gitignore
├── package-lock.json
├── package.json
├── README.md
└── yarn.lock
```

- Import `Link` from `react-router-dom`
- Wrap a `button` inside of `Link` tag

After making the changes, the `ShowTodoComponent` will look something like this.

{% gist https://gist.github.com/mritunjaysaha/44d332fd1aba601966e929db8514aa2e %}

#### v. Creating the `UpdateTodo` component to send `UPDATE` request

```
.
├── node_modules
├── public
├── src
│   ├── components
│   │   ├── createTodo.jsx
│   │   ├── showTodoList.jsx
│   │   └── updateTodo.jsx <-- we are here
│   ├── App.js
│   ├── App.scss
│   ├── App.test.js
│   ├── index.js
│   ├── reportWebVitals.js
│   └── setupTests.js
├── .gitignore
├── package-lock.json
├── package.json
├── README.md
└── yarn.lock
```

- Import `useState` from `react`
- Import `axios` from `axios`

The `UpdateTodo` component will have 3 props

- \_id
- handleClose
- handleEdited

The `updateTodo.jsx` file may look something like this.

{% gist https://gist.github.com/mritunjaysaha/b4ddd9265067cd7f2f801760d5deb9dc %}

#### vi. Adding the method to `DELETE` a todo

```
.
├── node_modules
├── public
├── src
│   ├── components
│   │   ├── createTodo.jsx
│   │   ├── showTodoList.jsx <-- we are here
│   │   └── updateTodo.jsx
│   ├── App.js
│   ├── App.scss
│   ├── App.test.js
│   ├── index.js
│   ├── reportWebVitals.js
│   └── setupTests.js
├── .gitignore
├── package-lock.json
├── package.json
├── README.md
└── yarn.lock
```

We will make the following changes in `showTodoList.jsx`

- Define a function `handleDelete` that will send a `DELETE` request to the server. This function will need the `_id` of the document to delete the document from the database. It will also update the array `todo` with the filtered array.
- Pass the `handleDelete` method as a prop to `TodoCard`
- Update `TodoCard` component to have the parameter `handleDelete`
- Add an `onClick` event for the button `delete` and pass the `handleDelete` method

After making the changes, the code will look something like this

{% gist https://gist.github.com/mritunjaysaha/63ca2f6e5a25ab7ef6d609d1b28ab352 %}

#### vii. Adding the `UpdateTodo` component in `showTodoList.jsx`

```
.
├── node_modules
├── public
├── src
│   ├── components
│   │   ├── createTodo.jsx
│   │   ├── showTodoList.jsx
│   │   └── updateTodo.jsx <-- we are here
│   ├── App.js
│   ├── App.scss
│   ├── App.test.js
│   ├── index.js
│   ├── reportWebVitals.js
│   └── setupTests.js
├── .gitignore
├── package-lock.json
├── package.json
├── README.md
└── yarn.lock
```

We need to add the following changes in the `showTodoList.jsx`

- Import `UpdateTodo` component from `updateTodo.jsx`
- Declare `open` using the `useState` hook with the default value of `false`. The value of `open` will be either `true` or `false`. We will conditionally render the `UpdateTodo` component. If the `edit` button is clicked on any one of the todo then we will set `open` to `true` when the `UpdateTodo` component will be rendered.
- Declare `id` using the `useState` hook. The `_id` of the todo document to be updated will be stored. It will be passed as a prop to `UpdateTodo` component.
- Declare `update` using the `useState` hook. This will be used to fetch all the todo documents from the database. Each time a todo document has been updated then `update` will change between `true` and `false`
- Define a function `handleEdit`. It will update the state `id` with the `_id` of the document and update the state of `open` to `true`. The `UpdateTodo` component will be rendered.
- Define a function `handleUpdate`. This will invert the state of `update` if the todo has been updated by the user. Inverting the state will cause the `useEffect` hook to update the `todo` array.
- Define a function `handleClose`. We need this to close the `UpdateTodo` component. This will set `id` to an empty string and set `open` to `false`.

Update the `TodoCard` component

- Pass the `handleEdit` function to the `TodoCard` component.
- Pass the `handleEdit` prop to the `edit` button.

After making the above changes, the code will look something like this

{% gist https://gist.github.com/mritunjaysaha/5ed65111d5ba98f3d21f5f46b673fbf6 %}

You can see the entire code for `part-2` in [GitHub](https://github.com/mritunjaysaha/todo/tree/master/client)