Home > Enterprise >  Re-render single element of an React component
Re-render single element of an React component

Time:12-06

I am working on a TO-DO List React App. The goal is, that every time the user clicks on the "Add" button the saveNote() function gets called and a new note is added to the notesobject. Then the page should automatically re-render and by that the addNote() function gets called and returns all notes within notes object as a new <li> item.

Right now adNote()only gets called after I clicked on the Addbutton and start entering a new note .. why?

// import Note from "./Note";

const notes = [
  {
    id: 1,
    content: "Test"
  }
];

let counter = 1;

function App() {
  const [note, setNote] = React.useState("");

  function createNote(event) {
    setNote(event.target.value);
  }

  function saveNote() {
    counter  ;
    notes.push({ id: counter, content: note });
    console.log(notes);
  }

  function addNote(note) {
    return (
      <li key={note.id}>
        <span>{note.content}</span>
      </li>
    );
  }

  return (
    <div className="container">
      <div className="heading">
        <h1>To-Do List</h1>
      </div>
      <div className="form">
        <input onChange={createNote} value={note} type="text" />
        <button onClick={saveNote} type="submit">
          <span>Add</span>
        </button>
      </div>
      <div>
        <ul>
          <li>A Item</li>
          {notes.map(addNote)}
        </ul>
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.body).render(<App />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>

CodePudding user response:

React components will re-render when they think that something may have changed that affects what should be displayed.

This is normally either the component's props (yours doesn't have any), or the component's state from useState or useReducer (and sometimes useContext).

For the component in your question, the only state your component "knows about" is the note state, corresponding to the value of your new note input. When you click "Save note", you change the notes array, but react doesn't "know" that this has happened or that it should re-render when that array changes.

When you subsequently type in the input field, you change the note state with the setNote call, which react does know about, and so it re-renders, re-calling the entire function, which then uses the latest values from the notes array, so everything updates. In a way, this is being fixed by a coincidence.

To fix the issue, you need to store the notes array in a react state, for example:

function App() {
  const [newNote, setNewNote] = React.useState("");
  const [notes, setNotes] = React.useState([]);

  function onNewNoteChanged(event) {
    setNewNote(event.target.value);
  }

  function saveNote() {
    counter  ;
    // Append the new note to the old array.
    setNotes((notes)=> [...notes, newNote]);
    setNewNote("");
    console.log(notes);
  }

  function addNote(note) {
    return (
      <li key={note.id}>
        <span>{note.content}</span>
      </li>
    );
  }

  return (
    <div className="container">
      <div className="heading">
        <h1>To-Do List</h1>
      </div>
      <div className="form">
        <input onChange={onNewNoteChanged} value={newNote} type="text" />
        <button onClick={saveNote} type="submit">
          <span>Add</span>
        </button>
      </div>
      <div>
        <ul>
          <li>A Item</li>
          {notes.map(addNote)}
        </ul>
      </div>
    </div>
  );
}

export default App;

CodePudding user response:

you are doing it wrong, react only re-renders on state or prop change. in this case your notes is a global list not a react state, change your notes list to React state and then on saveNote() function you can do something like this. we are spreading the old notes list and appending a new item in the list.

also note I am making increment on id as react keys should always be unique so react should be able to track which dom element has been changed.

function saveNote() {
    setNotes([...notes, { id: notes.length   1, content: note }]);
}

CodePudding user response:

In your example, you'd like to associate the useState capability using Array of Notes.

First, let's see how to use the useState() hook for creating an array state variable.

import { useState} from "react";

...

const [myArray, setMyArray] = useState([]);

The initial value of your array would be an empty array []. You can't update the array directly without using the method returned from useState(). In our case, it's setMyArray().

Use the update method to update the state with a new array that's created by combining the old array with the new element using JavaScript Spread operator.

setMyArray(oldArray => [...oldArray, newElement]);

The function will have the old array as a first parameter Hope its help :)

CodePudding user response:

import React from "react";
const notes = [
  {
    id: 1,
    content: "Test"
  }
];

let counter = 1;

function App() {
  const [note, setNote] = React.useState("");
  const [notesState, setNotes] = React.useState(notes);

  function createNote(event) {
    setNote(event.target.value);
  }

  function saveNote() {
    counter  ;
    setNotes((nt) =>[...nt , { id: counter, content: note }]);
    console.log(notesState);
  }

  function addNote(note) {
    return (
      <li key={note.id}>
        <span>{note.content}</span>
      </li>
    );
  }

  return (
    <div className="container">
      <div className="heading">
        <h1>To-Do List</h1>
      </div>
      <div className="form">
        <input onChange={createNote} value={note} type="text" />
        <button onClick={saveNote}>
          <span>Add</span>
        </button>
      </div>
      <div>
        <ul>
          <li>A Item</li>
          {notes.map(addNote)}
        </ul>
      </div>
    </div>
  );
}

export default App;
  • Related