25
MERN stack TODO application [Frontend]
We'll be creating the frontend application for our backend application.
In part-1, we
- Initialized backend using
npm
and installed necessary dependencies - Set up
MongoDB
database - Set up a server using
node.js
andexpress.js
- Created database
schema
to define aTODO
- Created api routes to
create
,read
,update
anddelete
todo
- Set up our frontend using
create-react-app
- Create components for reading all the todo, creating todo and updating todo
- 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
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.
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.
Within the client folder install the following dependencies
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.
{
"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"
]
}
}
- Delete the
logo.svg
- Remove the imports from
App.js
- Remove the following from
App.js
<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.
<header>Hello, World!</header>
Delete the
index.css
file and remove the corresponding import fromindex.js
Rename the
App.css
file toApp.scss
and change the corresponding import atApp.js
import "./App.scss" //updated
Run
npm start
. Openhttp://localhost:3000
and it should displayHello, World!
Copy and paste the styles from here and paste it in the
App.scss
file.
Now, we are good to start creating the frontend application.
.
├── 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
.
├── 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
anduseEffect
hooks fromreact
- Import
axios
fromaxios
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
We will import ShowTodoList
component in the App.js
file
The contents of the App.js
file should look something like this
Now, start the server
that we built in part-1
npm run dev
Now, start the client
side application
npm start
Open http://localhost:3000
in your browser and it should display all the todo documents that was fetched from the database.
.
├── 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
hookreact
- Import
Link
fromreact-router-dom
- Define a function
handleChange
that will get the input data - Define a function
handleSubmit
that will send thePOST
request to theserver
- Declare
data
usinguseState
hook with the following 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 statedata
After adding the above change the code will look something like this
.
├── 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
andRoute
fromreact-router-dom
- Import
CreateTodo
component fromcomponents/createTodo
- Create a
Route
for home page/
and pass theShowTodoList
component - Create a
Route
for creating a new todo/create-todo
- Wrap the
Route
s inside of theBrowserRouter
After making the changes the App.js
file should look something like this
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.
.
├── 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
fromreact-router-dom
- Wrap a
button
inside ofLink
tag
After making the changes, the ShowTodoComponent
will look something like this.
.
├── 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
fromreact
- Import
axios
fromaxios
The UpdateTodo
component will have 3 props
- _id
- handleClose
- handleEdited
The updateTodo.jsx
file may look something like this.
.
├── 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 aDELETE
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 arraytodo
with the filtered array. - Pass the
handleDelete
method as a prop toTodoCard
- Update
TodoCard
component to have the parameterhandleDelete
- Add an
onClick
event for the buttondelete
and pass thehandleDelete
method
After making the changes, the code will look something like this
.
├── 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 fromupdateTodo.jsx
- Declare
open
using theuseState
hook with the default value offalse
. The value ofopen
will be eithertrue
orfalse
. We will conditionally render theUpdateTodo
component. If theedit
button is clicked on any one of the todo then we will setopen
totrue
when theUpdateTodo
component will be rendered. - Declare
id
using theuseState
hook. The_id
of the todo document to be updated will be stored. It will be passed as a prop toUpdateTodo
component. - Declare
update
using theuseState
hook. This will be used to fetch all the todo documents from the database. Each time a todo document has been updated thenupdate
will change betweentrue
andfalse
- Define a function
handleEdit
. It will update the stateid
with the_id
of the document and update the state ofopen
totrue
. TheUpdateTodo
component will be rendered. - Define a function
handleUpdate
. This will invert the state ofupdate
if the todo has been updated by the user. Inverting the state will cause theuseEffect
hook to update thetodo
array. - Define a function
handleClose
. We need this to close theUpdateTodo
component. This will setid
to an empty string and setopen
tofalse
.
Update the TodoCard
component
- Pass the
handleEdit
function to theTodoCard
component. - Pass the
handleEdit
prop to theedit
button.
After making the above changes, the code will look something like this
You can see the entire code for part-2
in GitHub
25