Thought Process Behind a React App - Note Making App

Thought Process Behind a React App - Note Making App

Source Code

Live Preview

In my first blog article, I will not provide a step by step process on how I built a basic react app (they are available all over the internet !!!), rather most of them will be about the thought process that went behind it to build the app, because that’s what matters in coding, the mindset.

Also I will not focus on CSS as the blog will become too long, it will deviate my intention to teach thought process, rather we all know that in CSS, the sky's the limit.

As usual, you create a react app with npx create-react-app {app-name}. And then you clean everything off to start coding.

Since the project is all about creating a note making app, we created a simple beginner-level application in React, where the users can write or create their notes, and they can save their written notes after clicking the save button and the notes will be visible within the note boxes in our UI. Users can also delete their notes by clicking a delete icon on the notes boxes. We can make a functionality such that the users can also search their notes by typing in the search box.

Given the problem statement, first we have to imagine how the the basic layout will be displayed when the site loads.

For me it was like this..

Note Taking - 1.png

The extra features like word limit and display mode can be added as well

Now we need to think about this in terms of components. Let the header, the search bar, and the input be 3 different components.

function App() {
return (
     <div className="container">
       <Header/>
       <Search/>
       <NotesList/>
     </div>
 );
}

Once we have created this, we need to think that when we input something in a component, and then render that data and the input becomes empty again to get a new value, it’s basically adding the data in a state and then displaying all of it and then updating it again and then displaying all the data along with the data added. And because this variable gets updated and renders accordingly, we make it a state.

 const [notes, setNotes] = useState([]);

Now we just need to update this state, and the rendered data will be updated automatically which we will see. Let’s focus on adding data as of now.

Below is how I would write code in NotesList Component

function NotesList() {
 return (
   <div className="notes-list">
     {/* we will render the data here */}
     <AddNote/>
   </div>
 );
}

Yes, we create another component where we will create the input element to input data and then update the state

Inside AddNote Component

function AddNote() { 
return (
   <div className="note new">
     <textarea
       cols="10"
       rows="8"
       placeholder="Add a New Note..."
     />
     <div className="note-footer">
       <small>250 Remaining</small>
       <button className="save">
         Save
       </button>
     </div>
   </div>
 );
}

We then create a state here which will get updated when we type something and then we use that state to submit the value in the notes state.

function AddNote({ handleAddNote }) {
 // state which updates as we type something in input
 const [noteText, setNoteText] = useState("");
 // we set the character limit in input element
 const charLimit = 250;
 // this function runs when we make a change input,
 function handleChange(e) {
   // this state updates only when the value is less than charLimit
   if (e.target.value.length <= charLimit) {
     setNoteText(e.target.value);
   }
 }
 // this runs when we submit the input
 // which updates the note state in app component
 // here the note state is brought via props
 // I am not required to show how props work!!!
 function onSubmit() {
   if (noteText.trim().length > 0) {
     handleAddNote(noteText);
     // finally we empty the input
     setNoteText("");
   }
 }
 return (
   <div className="note new">
      {/* here whatever change happens in input,
      it is reflected again in input
      meaning it will stop when characters are more than 250 */}
     <textarea
       cols="10"
       rows="8"
       placeholder="Add a New Note..."
       value={noteText}
       onChange={handleChange}
     />
     <div className="note-footer">
       {/* And this is how the character value decreases when we */}
       {/* type something in input */}
       <small>{charLimit - noteText.length} Remaining</small>
       <button className="save" onClick={onSubmit}>
         Save
       </button>
     </div>
   </div>
 );
}

Now the notes state in app component is updated as follows

 function addNote(text) {
   // date created the moment the data is submitted
   const date = new Date();
   // a object is created to store metadata as well
   const newNote = {
     id: nanoid(),
     text: text,
     date: date.toLocaleDateString(),
   };
   // then we update it by adding newNote as well as previous ones
   const newNotes = [...notes, newNote];
   // now the new data is passed which will update the note state
   setNotes(newNotes);
 }

Using this note state, we pass this notes state via props to

<NotesList handleAddNote={addNote} notes={notes} />

Then in NotesList Component

function NotesList({ handleAddNote, notes }) {
 return (
   <div className="notes-list">
     {/* we will render the data here */}
     {notes.map((note) => {
       return <Note {...note}/>;
     })}
     <AddNote handleAddNote={handleAddNote} />
   </div>
 );
}

Then in Each Note Component

import { MdDeleteForever } from "react-icons/md";

function Note({ id, text, date, handleDelete }) {
 // accessing data via props and then render it
 return (
   <div className="note">
     {/* we render it here */}
     <p>{text}</p>
     <div className="note-footer">
       <small>{date}</small>
       {/* we then use this react-icon to trigger a function
       which will delete this component in the App Component, 
       thus deleting that note
       */}
       <MdDeleteForever
         className="delete-icon"
         size="1.3em"
         onClick={() => handleDelete(id)}
       />
     </div>
   </div>
 );
}

In App Component, we work in the triggered deletingNote function where

 function deletingNote(id) {
   // we filter it and store it
   const newNotes = notes.filter((note) => note.id !== id);
   // and then update it, thus rendering updated data
   setNotes(newNotes);
 }

Finally we focus on search functionality

In Search Component

function Search({ setSearchNote }) {
 return (
   <div className="search">
     {/* we add the search icon */}
     <MdSearch className="search-icon" size="1.4em" />
     {/* Here, when there is a change in input, 
     there is change in searchNote state */}
     <input
       type="text"
       placeholder="Search for your notes..."
       onChange={(e) => setSearchNote(e.target.value)}
     />
   </div>
 );
}

Then in App Component

     <div className="container">
       <Header handleToggleDarkMode={setDarkMode} />
       <Search setSearchNote={setSearchNote} />
        // as the states updates, so also this filtered value which 
        // then is passed via props displaying whatever we 
        // searched
       <NotesList
         handleAddNote={addNote}
         notes={notes.filter((note) =>
           note.text.toLowerCase().includes(searchNote.toLowerCase())
         )}
         handleDelete={deletingNote}
       />
     </div>

Finally, we have a basic app which will help us to take notes. You can add features like dark mode by toggling css classes by toggling states respectively or storing the notes in local storage when notes are updated and then displaying the same even when the file is closed and reopened.

Hope this blog article helped you to understand how to think when you are going to build an idea with the help of react. As you can see, even an app which looks complex can be built with the right mindset, so I am confident that it can build yours too. This is Biswas Signing out, Until we meet again.

P.S - In case you are the guy who skipped here. Let me tell you, this article may feel big, but 50% of the time, it is just code. So do check it out.