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
npmand installed necessary dependencies - Set up
MongoDBdatabase - Set up a server using
node.jsandexpress.js - Created database
schemato define aTODO - Created api routes to
create,read,updateanddeletetodo
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.
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
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"
]
}
}
3. Cleaning the src folder
- 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.cssfile and remove the corresponding import fromindex.jsRename the
App.cssfile toApp.scssand change the corresponding import atApp.js
import "./App.scss" //updated
Run
npm start. Openhttp://localhost:3000and it should displayHello, World!Copy and paste the styles from here and paste it in the
App.scssfile.
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.jsxshowTodoList.jsxupdateTodo.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
useStateanduseEffecthooks fromreact - Import
axiosfromaxios
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.
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
useStatehookreact - Import
Linkfromreact-router-dom - Define a function
handleChangethat will get the input data - Define a function
handleSubmitthat will send thePOSTrequest to theserver - Declare
datausinguseStatehook 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
POSTrequest 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
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
BrowserRouterandRoutefromreact-router-dom - Import
CreateTodocomponent fromcomponents/createTodo - Create a
Routefor home page/and pass theShowTodoListcomponent - Create a
Routefor creating a new todo/create-todo - Wrap the
Routes 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.
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
Linkfromreact-router-dom - Wrap a
buttoninside ofLinktag
After making the changes, the ShowTodoComponent will look something like this.
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
useStatefromreact - Import
axiosfromaxios
The UpdateTodo component will have 3 props
- _id
- handleClose
- handleEdited
The updateTodo.jsx file may look something like this.
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
handleDeletethat will send aDELETErequest to the server. This function will need the_idof the document to delete the document from the database. It will also update the arraytodowith the filtered array. - Pass the
handleDeletemethod as a prop toTodoCard - Update
TodoCardcomponent to have the parameterhandleDelete - Add an
onClickevent for the buttondeleteand pass thehandleDeletemethod
After making the changes, the code will look something like this
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
UpdateTodocomponent fromupdateTodo.jsx - Declare
openusing theuseStatehook with the default value offalse. The value ofopenwill be eithertrueorfalse. We will conditionally render theUpdateTodocomponent. If theeditbutton is clicked on any one of the todo then we will setopentotruewhen theUpdateTodocomponent will be rendered. - Declare
idusing theuseStatehook. The_idof the todo document to be updated will be stored. It will be passed as a prop toUpdateTodocomponent. - Declare
updateusing theuseStatehook. This will be used to fetch all the todo documents from the database. Each time a todo document has been updated thenupdatewill change betweentrueandfalse - Define a function
handleEdit. It will update the stateidwith the_idof the document and update the state ofopentotrue. TheUpdateTodocomponent will be rendered. - Define a function
handleUpdate. This will invert the state ofupdateif the todo has been updated by the user. Inverting the state will cause theuseEffecthook to update thetodoarray. - Define a function
handleClose. We need this to close theUpdateTodocomponent. This will setidto an empty string and setopentofalse.
Update the TodoCard component
- Pass the
handleEditfunction to theTodoCardcomponent. - Pass the
handleEditprop to theeditbutton.
After making the above changes, the code will look something like this
You can see the entire code for part-2 in GitHub
Top comments (5)
Faced some issues with " component={ShowTodoList} " as well as not enclosing with
<Routes> </Routes>Had to make the changes mentioned for this to work.
Thanks for the great tutorial by the way❤.
Great work ! But in the first part of ShowTodoList component you named the main function "ShowAllTodo()" instead of "ShowTodoList()" .Please try to fix it.
Somehow i run some issues with the update and delete functions. They don't do, what they should.
Maybe somebody has the same problem?
I have a problem with the handleDelete function in showTodoList component. I keep getting a 404 axios error and the function only deletes the title and description from the mongodb record.
I've checked the routing to my localhost and it looks correct.
http://localhost:8000/api/todo/${e.target.name}but still no love.