Live Site URL: https://keeper-app-basuabhirup.vercel.app
This project is a part of The Complete 2021 Web Development Bootcamp by London App Brewery, instructed by Dr. Angela Yu.
- To build a custom keeper app from the starting files
- To understand components, states and hooks in React
- To manage complex states and component tree in React
- Downloaded the starting files and installed the requiring dependencies using
npm install
command. - Created 4 components -
App
,Header
,Note
&Footer
and edited them accordingly to render the corresponding elements inReactDOM
. - Rendered all the notes inside
src/notes.js
file dynamically as separate Note components.
import notes from "../notes";
{notes.map((note) => (
<Note title={note.title} content={note.content} />
))}
- Added a new
InputArea
component insideApp
so that users can submit new notes. - Added a
DELETE
button inside eachNote
component so that users can click that to delete a particular note. - Created a new state variable
note
usingReact.useState()
hook insideInputArea
component to keep track of the changes made in the input fields.
const [note, setNote] = useState({
title: "",
content: ""
})
function updateNote(e) {
const name = e.target.name;
const value = e.target.value;
setNote({
...note,
[name]: value
})
}
<input name="title" onChange={updateNote} placeholder="Title" value={note.title} />
<textarea name="content" onChange={updateNote} placeholder="Take a note..." rows="3" value={note.content} />
- Created another state variable
notes
usingReact.useState()
hook insideApp
component to save all the notes inside an array:
const [notes, setNotes] = useState([]);
function addNote(note) {
setNotes([...notes, note]);
}
- Passed the
addNote
function fromApp
toInputArea
, so that a newly added note can be passed back fromInputArea
toApp
component:
// Code inside 'components/App.jsx' file:
<InputArea onSubmit={addNote}/>
// Code inside 'components/InputArea.jsx' file:
<form onSubmit={ (e) => {
props.onSubmit(note);
e.preventDefault();
setNote({ title: "", content: "" });
} }>
.....
</form>
- Disconnected the
notes.js
file from theApp
component. - Rendered all the notes stored inside the
notes
array dynamically as separate Note components:
{notes.map((note) => (
<Note title={note.title} content={note.content} />
))}
- Created a function inside the
App
component to delete any particular note and passed it to its childNote
component. Also, modified themap()
method to pass theindex
value asid
of each individualNote
component, so that it can be used to identify any particular note that needs deletion:
function deleteNote(id) {
setNotes(prevArray => {
return prevArray.filter((note, index) => index !== id);
})
}
{notes.map((note, index) => (
<Note key={`SGBPQ${index}`} id={index} onClick={deleteNote} title={note.title} content={note.content} />
))}
- Modified the code inside the
Note
component, so that after receiving props from its parentApp
component it can use them properly to delete any particular note:
<button onClick={ () => props.onClick(props.id)}>DELETE</button>
- Added two dependencies from Material-UI using the
npm i @material-ui/icons @material-ui/core
command from the terminal. - Imported
@material-ui/icons/Add
,@material-ui/icons/Delete
,@material-ui/core/Fab
and@material-ui/core/Zoom
elements and used them inside corresponding components to improve the UI. - Added another state variable
isClicked
usingReact.useState()
hook insideInputArea
component to manage its custom expansion functionality:
const [isClicked, setIsClicked] = useState(false);
<form onSubmit={setIsClicked(false); ....... } ....... />
.......
{isClicked && <input name="title" ....../>}
.......
<textArea name="content" onClick={ () => setIsClicked(true)} rows={isClicked ? "3" : "1"} ......./>
.......
<Zoom in={isClicked}>
<Fab type="submit"><AddIcon /></Fab>
</Zoom>
- Added a transparent texture background to the body of the webpage.
- Created a separate
server
directory, initialized NPM withnpm init -y
and installed necessary dependencies usingnpm i express mongoose cors dotenv
command from the terminal. - Created a
server.js
file and required all the necessary dependencies:
// Require necessary NPM modules:
require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const cors = require("cors");
// Assign an appropriate port for the server to listen:
const port = process.env.PORT || 5000;
// Initial Middleware setup:
const app = express();
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.use(cors());
// Connect to a new MongoDB Database, using Mongoose ODM:
// Create a new collection to store the notes:
// Handle HTTP requests:
// Enable the server to listen to the port:
app.listen(port, () => {
console.log(`Server started on port ${port}`);
})
- Connected the server to a Mongo Atlas database
keeperDB
and a collection namednotes
:
// Connect to a new MongoDB Database, using Mongoose ODM:
mongoose.connect(`mongodb+srv://${process.env.DB_USER}:${process.env.DB_PASSWORD}@cluster0.m5s9h.mongodb.net/keeperDB`);
mongoose.connection.once('open', () => {
console.log(`Connected to Mongo Atlas Database`);
})
// Create a new collection to store the notes:
const noteSchema = new mongoose.Schema ({
title: String,
content: String
}, {
timestamps: true
})
const Note = mongoose.model('Note', noteSchema);
- Set API endpoints to handle HTTP
GET
,POST
andDELETE
requests:
// Handle HTTP requests:
// Handle 'GET' requests made on the '/' route:
app.get('/', (req, res) => {
res.json({Connection: "Succesfully Established !"})
});
// Handle 'GET' requests made on the '/api/notes' route to get all notes:
app.get('/api/notes', (req, res) => {
Note.find({}, (err, notes) => {
if(!err) {
res.json(notes);
} else {
res.status(400).json({"error": err});
}
})
})
// Handle 'POST' requests made on the '/api/note/add' route to add a note:
app.post('/api/note/add', (req, res) => {
const note = new Note(req.body);
note.save(err => {
if(!err) {
res.redirect('/api/notes');
} else {
res.status(400).json({"error": err});
}
})
})
// Handle 'DELETE' requests made on the '/api/note/delete' route to delete a particular note:
app.delete('/api/note/delete', (req, res) => {
const id = req.body.objId;
Note.findByIdAndRemove(id, err => {
if(!err) {
res.json(`Deleted note with id: ${id} !`);
} else {
res.status(400).json({"error": err});
}
})
})
- Deployed the server at
https://keeper-app-backend-basuabhirup.vercel.app
- Finally, deployed the frontend portion at
https://keeper-app-basuabhirup.vercel.app