DEV Community

Cover image for Tracking changes on inputs and textareas in React
Paul Vitalis
Paul Vitalis

Posted on

Tracking changes on inputs and textareas in React

This post will cover how to track changes from an input field or text area. You can either follow along and write the code following the steps I will explain or just access the project on Stackblitz using the link I've provided and skip to the explanation.

Stackblitz project

1. Writing the code
We'll begin by creating a react app using vite

npm create vite@latest

Install all dependencies using:
npm install

Now in App.tsx, replace all the code with the following block of code

import { useState } from "react";
import "./App.css";

interface Note {
  id: number;
  text: string;
  backgroundColor: string;
  date: string;
}

function App() {
  const getRandomColor = (): string => {
    return "hsl(" + Math.random() * 360 + ", 100%, 75%)";
  };

  const [notes, setNotes] = useState<Note[]>([]);

  const [note, setNote] = useState<string>("");

  const [showModal, setShowModal] = useState<boolean>(false);
  const [showError, setShowError] = useState<boolean>(false);
  const addNote = () => {
    if (note.trim().length < 10) {
      setShowError(true);
      return;
    }

    setNotes([
      ...notes,
      {
        id: Math.random(),
        text: note.trim(),
        date: new Date().toLocaleDateString(),
        backgroundColor: getRandomColor(),
      },
    ]);

    toggleModal();
  };

  const handleChange = (event: any) => {
    setNote(event.target.value);
  };

  const resetForm = () => {
    setNote("");
    setShowError(false);
  };

  const toggleModal = () => {
    resetForm();
    setShowModal(!showModal);
  };

  return (
    <>
      {showModal && (
        <div className="modal">
          <div className="modal-content">
            <textarea
              className="text"
              onChange={handleChange}
              value={note}
            ></textarea>
            {showError && (
              <p className="error">Minimum characters allowed is 10</p>
            )}
            <div className="buttons">
              <button className="add-btn" onClick={addNote}>
                Add Note
              </button>
              <button className="close-btn" onClick={toggleModal}>
                Close
              </button>
            </div>
          </div>
        </div>
      )}

      <main>
        <div className="header">
          <h2 className="header-title">Notes</h2>
          <button className="add-note-btn" onClick={toggleModal}>
            +
          </button>
        </div>
        <div className="notes-container">
          {notes.map((note) => {
            return (
              <div
                key={note.id}
                className="note"
                style={{ backgroundColor: note.backgroundColor }}
              >
                <p className="note-text">{note.text}</p>
                <p className="note-date">{note.date}</p>
              </div>
            );
          })}
        </div>
      </main>
    </>
  );
}

export default App;

Enter fullscreen mode Exit fullscreen mode

Update the App.css to be as follows

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.add-note-btn {
  border: None;
  background-color: black;
  color: white;
  border-radius: 50%;
  padding: 10px 13px;
}

.add-note-btn:hover {
  cursor: pointer;
  box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
}


main {
  width: 80%;
  margin: 0 auto;
}

.notes-container {
  display: flex;
  flex-wrap: wrap;
}

.note {
  width: 200px;
  height: 200px;
  border-radius: 10px;
  margin-right: 15px;
  margin-bottom: 20px;
  padding: 10px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}

.note-text {
  height: 80%;
  overflow: scroll;
  -ms-overflow-style: none;  /* IE and Edge */
  scrollbar-width: none;  /* Firefox */
  word-wrap: break-word;
}

/* Hide scrollbar for Chrome, Safari and Opera */.note-text::-webkit-scrollbar {
  display: none;
}

.modal {
  position: fixed; /* Stay in place */
  z-index: 1; /* Sit on top */
  left: 0;
  top: 0;
  width: 100%; /* Full width */
  height: 100%; /* Full height */
  overflow: auto; /* Enable scroll if needed */
  background-color: rgb(0,0,0); /* Fallback color */
  background-color: rgba(0,0,0,0.4); /* Black w/ opacity */
}

/* Modal Content/Box */
.modal-content {
  background-color: #fefefe;
  margin: 15% auto; /* 15% from the top and centered */
  padding: 20px;
  border: 1px solid #888;
  width: 40%; /* Could be more or less, depending on screen size */
  border-radius: 10px;
}

/* The Close Button */
.close {
  color: #aaa;
  float: right;
  font-size: 28px;
  font-weight: bold;
}

.close:hover,
.close:focus {
  color: black;
  text-decoration: none;
  cursor: pointer;
}

.buttons {
  display: flex;
  flex-direction: column;
}

.buttons button {
  margin-top: 8px;
  color: #fff;
  border: none;
  padding: 5px 0;
  font-size: 18px;
  border-radius: 4px;
}

.buttons button:hover {
  cursor: pointer;
}

textarea {
  width: 100%;
  min-height: 100px;
  font-size: 18px;
}

.add-btn {
  background-color: purple;
}

.close-btn {
  background-color: rgb(133, 34, 34);
}

.error {
  color: red;
}

@media only screen and (max-width: 500px) {
  .modal-content {
    width: 80%; /* Could be more or less, depending on screen size */

  }
}
Enter fullscreen mode Exit fullscreen mode

Finally, Update the index.css file too

:root {
  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
  /* line-height: 1.5; */
  font-weight: 400;

  color-scheme: light dark;
  color: rgba(255, 255, 255, 0.87);
  background-color: #242424;

  font-synthesis: none;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a:hover {
  color: #535bf2;
}

body {
  margin: 0;
  place-items: center;
  min-width: 320px;
  min-height: 100vh;
}


@media (prefers-color-scheme: light) {
  :root {
    color: #213547;
    background-color: #ffffff;
  }
  a:hover {
    color: #747bff;
  }
  button {
    background-color: #f9f9f9;
  }
}

Enter fullscreen mode Exit fullscreen mode

Save all the changes and run your app with the following command:

npm run dev

Your application should now look as follows:

Initial application view

Our application is a notes app that allows us to add notes.

On clicking the + icon, we get a modal as follows:

Modal

2. Explanation.

The textarea field has an initial value set to the **note **variable that we created:

<textarea
              className="text"
              onChange={handleChange}
              value={note}
            ></textarea>
Enter fullscreen mode Exit fullscreen mode

JavaScript allows us to listen to an input’s change in value by providing the attribute ** onchange**. React’s version of the onchange event handler is the same but camel-cased.

The handleChange function used on the onChange event handler was defined as follows:

 const handleChange = (event: any) => {
    console.log(event.target.value)
    setNote(event.target.value);
    console.log("Note: ", note)
  };

Enter fullscreen mode Exit fullscreen mode

You can check the console and see the value that is logged out and also the value of the note as you type.

Console output

The handleChange function simply sets the note variable to be whatever text the user inputs as they type.

And that's it. On change is a very useful event handler and will simplify your work relating to user input on your React journey.

For the source code, you can access it at this link: Source code

Follow me on:
LinkedIn: Paul Otieno
Twitter: me_huchoma
Instagram: paul_dreamer_

Happy Coding! 🥂

Top comments (0)