Table of Contents
In this last lesson of the course, we will create a note-taking app to apply many React concepts from the previous lessons. With this app, you can create notes and edit/view/delete them.
We will utilize the concepts of hooks, functional components, react-router to switch between pages, React context API to store the notes, and allow updates, edits, and delete. The buttons will have event listeners to detect clicks and perform actions.
By the end, you will learn to organize a proper project structure and understand how the pieces of React work together.
Setting up our project structure
The first step to create any React project is to use the create-react-app tool from the official React team. If you have this tool globally installed, you can run:
create-react-app note-taking-app
Otherwise, you can run this:
npx create-react-app note-taking-app
It will set up the React project with the proper folder structure and install the essential packages like react, react-dom, etc.
All of our source code will live inside the src folder. Inside the src folder, you will find App.js, which will be our root component.
To run the project, type this command:
npm start
It will display a default welcome page. Now our React environment is ready, and we can start coding our Note-taking app.
Setting up the routers
The note-taking app will have different pages and routes. The react-router will handle these all.
1. Install the react-router-dom to our project:
npm install react-router-dom
2. Wrap the App component inside the <BrowserRouter /> to use the routing features.
src/index.js import ReactDOM from 'react-dom' import './index.css' import App from './App' import { BrowserRouter } from 'react-router-dom' ReactDOM.render( <BrowserRouter> <App /> </BrowserRouter>, document.getElementById('root') )
3. Create a router component for our application.
We will have four routes for this app-
- The Root Route(/): To display a form to create a note.
- The Notes Route(/notes): All the created notes will be listed on this page.
- A note page(/notes/:id): To display a single note for a given :id.
- A note edit page(/notes/edit/:id): To open a form to edit the note.
Router code
To create all of these different routes, we need to write the react-router code. So, create a components folder inside src. Inside that, create another folder “router”. Then, create a router.component.jsx file and include this router code:
src/comopnents/router/router.component.jsx
import React, { Component } from 'react' import { Route, Switch } from 'react-router-dom' class Router extends Component { render() { return ( <div> <Switch> <Route path='/' exact> <h1>Route path / </h1> </Route> <Route path='/notes' exact> <h1>Route path /notes</h1> </Route> <Route path='/notes/:id' exact> <h1>Route path /notes/:id</h1> </Route> <Route path='/notes/edit/:id'> <h1>Route path /notes/edit/:id</h1> </Route> </Switch> </div> ) } } export default Router
Connecting the Router with the root App component.
The router file is just an independent entity here. To use it, we need to import and render this router component in App.js.
src/App.js
import React from 'react' import './App.css' import Router from './components/router/router.component' class App extends React.Component { render() { return ( <div className='App'> <Router /> </div> ) } } export default App
We have connected the Router component with the App component. Now, if you save your code, the compiler should show the output in the browser.
Try manually changing the browser URLs one by one and check the outputs according to the Router code that we just wrote:
- / should render <h1>Route path /</h1>
- /notes should render <h1>Route path /notes</h1>
- /notes/34 should render <h1>Route path /notes/34</h1>
- /notes/edit/22 should render <h1>Route path /notes/edit/22</h1>
Creating the header component to switch between / and /notes routes
The users won’t have to manually update the browser URL to use the app. For that, we will need create a header component to display two links: note and my notes.
So, inside the component folder, create a folder “header” then header.component.jsx inside it and add this code:
src/header/header.component.jsx
import React, { Component } from 'react' import { Link } from 'react-router-dom' /* create your header style css in the same header folder */ import './header.styles.css' import { withRouter } from 'react-router-dom' class Header extends Component { render() { const { location } = this.props function setActive(path, exact) { if (exact) return location.pathname === path && 'active' return location.pathname.startsWith(path) && 'active' } return ( <div className='header'> <Link to='/' className={`link ${setActive('/', true)}`}> Note </Link> <Link to='/notes' className={`link ${setActive('/notes')}`}> My Notes </Link> </div> ) } } export default withRouter(Header)
We have used the Link component to navigate from one Page to another without refreshing the page. The setActive function detects if the current location matches the URL to apply the active green color.
Now, import and render the Header component inside the app:
src/App.js
import Header from './components/header/header.component' /* previous App.js code */ return ( <div className='App'> <Header> <Router> </div> ) /* rest of the code including export */
Now clicking on the header links will help you to navigate from one route to another.
Coding the Create Note page
It’s time to replace the “Route path /” output with an actual page component that will display to the user a form to create a note.
Now, create a note.component.jsx file inside pages/note folder. Components that are used in the Router file should be separately stored inside different folder-like pages.
Study and add the following code to the note component which displays a form:
src/pages/note/note.component.jsx
import React, { useState } from 'react' import './note.styles.css' const NotePage = props => { const [title, setTitle] = useState('') const [text, setText] = useState('') const handleSubmit = e => { /* to avoid browser refresh on form submit */ e.preventDefault() /* we will add functionality later to add notes to the note context */ history.push('/notes') } return ( <div> <h1>Create a note</h1> <form className='form' onSubmit={handleSubmit}> <input className='form-input' type='text' placeholder='enter note title' value={title} onChange={e =>setTitle(e.target.value)} required /> <textarea className='form-textarea' placeholder='type your note...' value={text} onChange={e =>setText(e.target.value)} required /> <input type='submit' className='form-submit' value=‘Save Note' /> </form> </div> ) } export default NotePage
The note page will display a form, and a save note button. We have used the form submit handler to handle the creation of notes.
It implements the concept of controlled forms where title and text state values are updated on any form change.
Now go to the Router component and import this NotePage and render it for the “/” route instead of <h1>Route path /</h1>
.
src/components/router/router.component.jsx
import NotePage from '../../pages/note/note.component' //… <Route path='/' exact> <NotePage /> </Route> //…
Now go to the / path, and the NotePage should be displayed.
Coding MyNotes page
The MyNotes page will display all the notes that we create. Even though we have not added the functionality to create notes, it is helpful to keep our UI and pages ready first.
Coding the NoteItem component
The NoteItem component will be a part of the MyNotes page component. It will be rendered and mapped for every note that is created.
Note Model
In the app, every note will be represented as an object with these properties:
/* { id: any positive number – 1, 2, 3,…, title: 'title of the note – a string', text: 'the text content of the note' } */
NoteItem’s main work is to take this note object and display some UI. We will also add edit and delete buttons.
Code for NoteItem component:
Create a note-item component inside compoents and include the code below:
src/components/note-item/note-item.component.jsx
import './note-item.styles.css' const NoteItem = props => { const { note } = props const { id, title, text } = note return ( <div className='note-item'> <div className='note-title'>{id}. {title}</div> <div className='note-btn-container'> <button className='note-btn edit-btn'> Edit </button> <button className='note-btn delete-btn'> Delete </button> </div> </div> ) } NoteItem.defaultProps = { note: { id: '', title: '', text: '' } } export default NoteItem
Code for MyNotes page:
Now, we can use the NoteItem component inside the MyNotes page. We have created two dummy note objects and passed them to NoteItem.
src/pages/mynotes/mynotes.component.jsx
import React, { Component } from 'react' import NoteItem from '../../components/note-item/note-item.component' import './mynotes.styles.css' function MyNotes() { /* dummy note objects */ const note1 = { id: 1, title: 'test 1', text: 'hello world note 1' } const note2 = { id: 2, title: 'other note', text: 'hello world note 2' } return ( <div> <h1>My Notes</h1> <div className='note-container'> <NoteItem note={note1} /> <NoteItem note={note2} /> </div> </div> ) } export default MyNotes
Right now, we are rendering NoteItem components manually one by one and passing them an object.
Later, the note objects will be stored inside an array in the note context API. We will then use the .map()
method to loop through the array and dynamically render the NoteItem components.
What we have done so far
- Organized our project folder and set up routers.
- Defined different routes and laid the foundation for our application.
- Created React components like Header and NoteItem to make them reusable.
- Created two Page components for the routes / and /notes, namely NotePage and MyNotes page. The router renders the two-page components.
You are now ready to move to part 2 of this project to work with logic and functionality for the app.
The Note-Taking App Project in React (Part 2)