1

I am building a clone of the Google Keep app with react js. I added all the basic functionality (expand the create area, add a note, delete it) but I can't seem to manage the edit part. Currently I am able to edit the inputs and store the values in the state, but how can I replace the initial input values for the new values that I type on the input?

This is Note component

export default function Note(props) {
  const [editNote, setEditNote] = useState(false);
  const [currentNote, setCurrentNote] = useState({
    id: props.id,
    editTitle: props.title,
    editContent: props.content,
  });

  const handleDelete = () => {
    props.deleteNote(props.id);
  };

  const handleEdit = () => {
    setEditNote(true);
    setCurrentNote((prevValue) => ({ ...prevValue }));
  };

  const handleInputEdit = (event) => {
    const { name, value } = event.target;

    setCurrentNote((prevValue) => ({
      ...prevValue,
      [name]: value,
    }));
  };

  const updateNote = () => {
    setCurrentNote((prevValue, id) => {
      if (currentNote.id === id) {
        props.title = currentNote.editTitle;
        props.content = currentNote.editContent;
      } else {
        return { ...prevValue };
      }
    });
    setEditNote(false);
  };

  return (
    <div>
      {editNote ? (
        <div className='note'>
          <input
            type='text'
            name='edittitle'
            defaultValue={currentNote.editTitle}
            onChange={handleInputEdit}
            className='edit-input'
          />
          <textarea
            name='editcontent'
            defaultValue={currentNote.editContent}
            row='1'
            onChange={handleInputEdit}
            className='edit-input'
          />
          <button onClick={() => setEditNote(false)}>Cancel</button>
          <button onClick={updateNote}>Save</button>
        </div>
      ) : (
        <div className='note' onDoubleClick={handleEdit}>
          <h1>{props.title}</h1>
          <p>{props.content}</p>
          <button onClick={handleDelete}>DELETE</button>
        </div>
      )}
    </div>
  );
}

And this is the Container component where I am renderind the CreateArea and mapping the notes I create. I tried to map the notes again with the new values but it wasn't working.

export default function Container() {
  const [notes, setNotes] = useState([]);

  const addNote = (newNote) => {
    setNotes((prevNotes) => {
      return [...prevNotes, newNote];
    });
  };

  const deleteNote = (id) => {
    setNotes((prevNotes) => {
      return prevNotes.filter((note, index) => {
        return index !== id;
      });
    });
  };

  // const handleUpdateNote = (id, updatedNote) => {
  //   const updatedItem = notes.map((note, index) => {
  //     return index === id ? updatedNote : note;
  //   });
  //   setNotes(updatedItem);
  // };

  return (
    <div>
      <CreateArea addNote={addNote} />
      {notes.map((note, index) => {
        return (
          <Note
            key={index}
            id={index}
            title={note.title}
            content={note.content}
            deleteNote={deleteNote}
            //handleUpdateNote={handleUpdateNote}
          />
        );
      })}
    </div>
  );
}
Oriana Abreu
  • 41
  • 1
  • 4

1 Answers1

0

There are a couple of mistakes in your code.

  1. The state properties are in the camel case
  const [currentNote, setCurrentNote] = useState({
    ...
    editTitle: props.title,
    editContent: props.content,
  });

But the names of the input are in lowercase.

          <input
            name='edittitle'
            ...
          />
          <textarea
            name='editcontent'
            ...
          />

Thus in handleInputEdit you don't update the state but add new properties: edittitle and editcontent. Change the names to the camel case.

  1. In React you cant assign to the component prop values, they are read-only.
  const updateNote = () => {
    ...
        props.title = currentNote.editTitle;
        props.content = currentNote.editContent;

You need to use the handleUpdateNote function passed by the parent component instead. You have it commented for some reason.

          <Note
            ...
            //handleUpdateNote={handleUpdateNote}
          />

Check the code below. I think it does what you need.

function Note({ id, title, content, handleUpdateNote, deleteNote }) {
  const [editNote, setEditNote] = React.useState(false);
  const [currentNote, setCurrentNote] = React.useState({
    id,
    editTitle: title,
    editContent: content,
  });

  const handleDelete = () => {
    deleteNote(id);
  };

  const handleEdit = () => {
    setEditNote(true);
    setCurrentNote((prevValue) => ({ ...prevValue }));
  };

  const handleInputEdit = (event) => {
    const { name, value } = event.target;
    setCurrentNote((prevValue) => ({
      ...prevValue,
      [name]: value,
    }));
  };

  const updateNote = () => {
    handleUpdateNote({
      id: currentNote.id,
      title: currentNote.editTitle,
      content: currentNote.editContent
    });
    setEditNote(false);
  };

  return (
    <div>
      {editNote ? (
        <div className='note'>
          <input
            type='text'
            name='editTitle'
            defaultValue={currentNote.editTitle}
            onChange={handleInputEdit}
            className='edit-input'
          />
          <textarea
            name='editContent'
            defaultValue={currentNote.editContent}
            row='1'
            onChange={handleInputEdit}
            className='edit-input'
          />
          <button onClick={() => setEditNote(false)}>Cancel</button>
          <button onClick={updateNote}>Save</button>
        </div>
      ) : (
        <div className='note' onDoubleClick={handleEdit}>
          <h1>{title}</h1>
          <p>{content}</p>
          <button onClick={handleDelete}>DELETE</button>
        </div>
      )}
    </div>
  );
}

function CreateArea() {
  return null;
}

function Container() {
  const [notes, setNotes] = React.useState([
    { title: 'Words', content: 'hello, bye' },
    { title: 'Food', content: 'milk, cheese' }
  ]);

  const addNote = (newNote) => {
    setNotes((prevNotes) => {
      return [...prevNotes, newNote];
    });
  };

  const deleteNote = (id) => {
    setNotes((prevNotes) => {
      return prevNotes.filter((note, index) => {
        return index !== id;
      });
    });
  };

  const handleUpdateNote = ({ id, title, content }) => {
    const _notes = [];
    for (let i = 0; i < notes.length; i++) {
      if (i === id) {
        _notes.push({ id, title, content });
      } else {
        _notes.push(notes[i]);
      }
    }
    
    setNotes(_notes);
  };

  return (
    <div>
      <CreateArea addNote={addNote} />
      {notes.map((note, index) => {
        return (
          <Note
            key={index}
            id={index}
            title={note.title}
            content={note.content}
            deleteNote={deleteNote}
            handleUpdateNote={handleUpdateNote}
          />
        );
      })}
    </div>
  );
}


function App() {
  return (
    <div>
      <Container />
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
<div id="root"></div>

Also, you can store the notes in an object or hash map instead of an array. For example

     const [notes, setNotes] = React.useState({
        'unique_id': { title: 'Words', content: 'hello, bye' }
      });

Then in handleUpdateNote you have

setNotes((prev) => ({ ...prev, unique_id: { title, content } }))

srgbnd
  • 5,404
  • 9
  • 44
  • 80
  • Thank you so much!! it worked. Also, I was reading about controlled and uncontrolled components because before applying your changes, the value property didn't allow me to type anything on the inputs. I applied your suggestions, change defaultValue to value and everything looks perfect. Thanks again! – Oriana Abreu Oct 21 '21 at 15:24
  • Actually I tried again creating different notes but when I edit one of them, the modifications are saved but the content of the other notes turns to undefined – Oriana Abreu Oct 21 '21 at 16:41
  • @OrianaAbreu I've fixed a typo in the code: `_notes.push(note)` -> `_notes.push(notes[i])` – srgbnd Oct 21 '21 at 17:01