Trusted answers to developer questions
Trusted Answers to Developer Questions

Related Tags

react
communitycreator

How to create a sticky notes app using the React useReducer hook

Nasreen

React has a vast collection of Hooks that make it easier for developers to access the props values in various components.

In this shot, we will learn the functions of React’s useReducer Hook. The useReducer Hook simply creates a store to keep track of the application state that you can then use create actions to be performedi.e., add/delete/update. Those actions can then be called using the dispatch method of the useReducer Hook.

Let’s go over these concepts more clearly through code. We begin with our Sticky Notes app, which will allow users to create and delete sticky notes. On the backend, we will be using the useReducer Hook to manage the state of the application.

First, we will start off by creating a new React app using the npx create-react-app my-react-app command. Then, we will do the necessary clean-up and go to the App.js file, where we will declare the initial state of the app. For this application, the initial state should have the following fields:

const initialState = {
  lastNoteCreatedAt: null,
  totalNotes: 0,
  notes:[]
}

Here is the description of these fields:

  • lastNoteCreatedAt: Displays the time when the last note was created.
  • totalNotes: Total number of notes to display on the header.
  • notes: Array of actual notes to store all of the notes.

Don’t forget to import the useReducer and useState Hooks at the top of the App.js file. This is shown below.

import React,{useState, useReducer} from 'react'

Next, let’s create a form and a text area where the user will enter their notes.

<form className="main-form" onSubmit={addNote}>
<textarea placeholder="Add Note" 
value={noteText}
onChange={(e)=>setNoteText(e.target.value)}
></textarea>
<button>Add</button>
 </form>

The value attribute in the text area corresponds to the state we will need by using the useState hook.

const [noteText, setNoteText] = useState('')

Now, let’s create our notesReducer, where we will define what actions will take place in our app.

const notesReducer = (prevState, action) => {
  switch(action.type){
    case 'ADD_NOTE':
      const newNote = {
        lastNoteCreatedAt: new Date().toTimeString().slice(0,8),
        totalNotes:prevState.notes.length +1,
        notes:[...prevState.notes, action.payload]
      }
      // {console.log(newNote)}
      return newNote;


    default:
    return prevState;
  }
}

This notesReducer contains an existing statecalled prevState in our case and an action attribute that corresponds to the actions the reducer can perform.

Our reducer’s first action is the ADD_NOTE action, which will create a new note with a time string and an array of existing notes. It also includes the newer entry and a record of total notes by adding one to the existing length of the notes array.

Now, in the app, we have to call this reducer in the manner shown below.

const [notesState, dispatch] = useReducer(notesReducer,initialState)

Our addNote() method, called when the form is submitted, needs to do the following:

  • Return without doing anything if the input is blank.
  • Create a new note with the contents you want to have in a note, such as: the idwe have used the uuid() package here to generate a unique id every time a note is created, the note text, and a rotate valuesolely for the styling purpose, it will slightly rotate each note with a different value.
  • Dispatch the newly created note to the reducer store to tell the action type is required on this note.
  • Set the note input to null again.
const addNote = (e) => {
e.preventDefault();

if(!noteText){
  return;
}

const newNote = {
  id: uuid(),
  text: noteText,
  rotate: Math.floor(Math.random()*20)
}

dispatch({ type:'ADD_NOTE', payload:newNote})
setNoteText('')
}

We will use the map method from Javascript to display our notes. This is shown below.

{notesState.notes.map((note)=> (
  <div className="note"
  style={{transform:`rotate(${note.rotate}deg)`}}
  key={note.id}
  draggable="true"
  onDragEnd={dropNote}
  >
 <h2 className="text">{note.text}</h2> 
 <button className="delete-btn" onClick={()=>deleteNote(note)}>X</button>
 </div>
  
))}

We have added the draggable="true" functionality in order to allow users to smoothly drag the notes to a new position. This will also require us to create the following two functions:

const dropNote = (e) => {
e.target.style.left = `${e.pageX - 50}px`;
e.target.style.top = `${e.pageY - 50}px`;
}

const dragOver = (e) => {
  e.stopPropagation();
  e.preventDefault();
}

This drag and drop functionality is beyond the context of this post, so I will not talk about it in much detail here, but you can find more details here.

Now, let’s write the DELETE_NOTE action, which will do the following:

  • Keep the previous state intact, i.e., don’t touch the existing array.
  • Reduce the total number of notes by one.
  • Filter the notes array and remove the one that has to be deleted.
case 'DELETE_NOTE':
      const deleteNote = {
        ...prevState,
        totalNotes: prevState.notes.length -1,
        notes: prevState.notes.filter(note=>note.id !== action.payload.id)

      }
      return deleteNote

We will call the DELETE_NOTE action in a deleteNote function. This is called on by clicking the delete button present with each of the notes:

const deleteNote = (id) => {
  console.log('delete')
dispatch({ type:'DELETE_NOTE', payload: id})
}

This brings an end to the code of our application. You can find the styling and complete code for this app here.

RELATED TAGS

react
communitycreator
RELATED COURSES

View all Courses

Keep Exploring