Learning React by creating Notes App - Part 1

Photo by Clark Tibbs on Unsplash

Learning React by creating Notes App - Part 1

·

6 min read

Table of contents

We're going to create a full-stack react application with CRUD features, in a series of blogs. In this blog, we will learn to create a basic front end for our application.

Before starting out, let's take the Web developer's oath.

  • I will have my browser developer console open all the time

  • I progress with small steps

  • I will write lots of console.log statements to make sure I understand how the code behaves and to help pinpoint problems. (What's the difference between an experienced JavaScript programmer and a rookie? The experienced one uses console.log 10-100 times more)

  • If my code does not work, I will not write more code. Instead, I start deleting the code until it works or just return to a state when everything still was still working

Very first install Node LTS version.

Move into whatever directory you want to get into, open the terminal, command prompt(in windows), and type

npx create-react-app <your-app-name>
cd <your-app-name>

You can delete every other file in the src directory apart from index.js and app.js, we won't be needing them and later we will make files that will help us do the same task.

Let's start with the following (the file App.js):

const App = (props) => {
  const { notes } = props

  return (
    <div>
      <h1>Notes</h1>
      <ul>
        <li>{notes[0].content}</li>
        <li>{notes[1].content}</li>
        <li>{notes[2].content}</li>
      </ul>
    </div>
  )
}

export default App

The file index.js should look like this:

import React from 'react'
import ReactDOM from 'react-dom/client'

import App from './App'

const notes = [
  {
    id: 1,
    content: 'HTML is easy',
    important: true
  },
  {
    id: 2,
    content: 'Browser can execute only JavaScript',
    important: false
  },
  {
    id: 3,
    content: 'GET and POST are the most important methods of HTTP protocol',
    important: true
  }
]

ReactDOM.createRoot(document.getElementById('root')).render(
  <App notes={notes} />
)

Every note contains its textual content, a boolean value for marking whether the note has been categorized as important or not, and also a unique id.

The example above works due to the fact that there are exactly three notes in the array.

A single note is rendered by accessing the objects in the array by referring to a hard-coded index number:

note: {anything you write here is Javascript}

<li>{notes[1].content}</li>

This is, of course, not practical. We can improve on this by generating React elements from the array objects using the map function.

notes.map(note => <li>{note.content}</li>)

The result is an array of li elements.

[
  <li>HTML is easy</li>,
  <li>Browser can execute only JavaScript</li>,
  <li>GET and POST are the most important methods of HTTP protocol</li>,
]

Which can then be placed inside ul tags:

const App = (props) => {
  const { notes } = props

  return (
    <div>
      <h1>Notes</h1>
      <ul>
        {notes.map(note => <li>{note.content}</li>)}
      </ul>
    </div>
  )
}

See this is the advantage of React, breaking large chunks of code into byte-sized logical and functional components.

Because the code generating the li tags is JavaScript, it must be wrapped in curly braces in a JSX template just like all other JavaScript code.

We will also make the code more readable by separating the arrow function's declaration across multiple lines:

const App = (props) => {
  const { notes } = props

  return (
    <div>
      <h1>Notes</h1>
      <ul>
        {notes.map(note => 
          <li>
            {note.content}
          </li>
        )}
      </ul>
    </div>
  )
}

as of now, our front end should look something like this.

Even though the application seems to be working, there is a nasty warning in the console:

As the linked React page in the error message suggests; the list items, i.e. the elements generated by the map method, must each have a unique key value: an attribute called key.

Let's add the keys:

const App = (props) => {
  const { notes } = props

  return (
    <div>
      <h1>Notes</h1>
      <ul>
        {notes.map(note => 
          <li key={note.id}>
            {note.content}
          </li>
        )}
      </ul>
    </div>
  )
}

And the error message disappears. React uses the key attributes of objects in an array to determine how to update the view generated by a component when the component is re-rendered. More about this is in the React documentation.

Map

Understanding how the array method mapworks is crucial for most web applications. The application contains an array called notes:

const notes = [
  {
    id: 1,
    content: 'HTML is easy',
    important: true
  },
  {
    id: 2,
    content: 'Browser can execute only JavaScript',
    important: false
  },
  {
    id: 3,
    content: 'GET and POST are the most important methods of HTTP protocol',
    important: true
  }
]

[1, 2, 3] will be printed to the console. map always creates a new array, the elements of which have been created from the elements of the original array by mapping: using the function given as a parameter to the map method.

Refactoring

Let's tidy the code up a bit. We are only interested in the field notes of the props, so let's retrieve that directly using destructuring:

const App = ({ notes }) => {
  return (
    <div>
      <h1>Notes</h1>
      <ul>
        {notes.map(note => 
          <li key={note.id}>
            {note.content}
          </li>
        )}
      </ul>
    </div>
  )
}

We'll separate displaying a single note into its own component Note:

const Note = ({ note }) => {
  return (
    <li>{note.content}</li>
  )
}

const App = ({ notes }) => {
  return (
    <div>
      <h1>Notes</h1>
      <ul>
        {notes.map(note => 
          <Note key={note.id} note={note} />
        )}
      </ul>
    </div>
  )
}

Now the important point here is that we can code a whole React application in a single file. Although that is, of course, not very practical. Common practice is to declare each component in its own file as an ES6-module.

We have been using modules the whole time. The first few lines of the file index.js:

import ReactDOM from "react-dom/client"

import App from "./App"

import two modules, enabling them to be used in that file. The module react-dom/client is into the variable ReactDOM, and the module that defines the main component of the app is placed into the variable App

Let's move our Note component into its own module.

In smaller applications, components are usually placed in a directory called components, which is in turn placed within the src directory. The convention is to name the file after the component.

Now, we'll create a directory called components for our application and place a file named Note.js inside. The contents of the Note.js file are as follows:

const Note = ({ note }) => {
  return (
    <li>{note.content}</li>
  )
}

export default Note

The last line of the module exports the declared module, the variable Note.

Now the file that is using the component - App.js - can import the module:

import Note from './components/Note'

const App = ({ notes }) => {
  // ...
}

The component exported by the module is now available for use in the variable Note, just as it was earlier.

Note that when importing our own components, their location must be given in relation to the importing file.

We will go over ReactDOM in some other blog.

In the next part, we will be creating a form for taking input from the user using React States.