Step 20: Edit Notes

Here is what we want:

  • When a user clicks on the edit icon, they should be redirected to the edit page.
  • The title and text of the note must be displayed in the editor.
  • The user should be able to make changes to the title and text.
  • When they click on the save button, their changes should be saved and they should be redirected to the homepage.

To implement this behaviour, we start with adding an edit function to App as follows:

const edit = (id, title, text) => {
  setNotes((notes) =>
    notes.map((note) => {
      if (note.id !== id) {
        return note;
      } else {
        return { id, title, text };
      }
    })
  );
};
  • Pass the edit function as props to the Edit component in App
  • Update the Edit component PropTypes specification to account for this addition.

When we navigate (redirect the user) to the edit page, we should pass the note attributes (the one to be edited). We can do this through useNavigation hook as follows. Make the following change to the components/NoteControl.jsx:

  const handleOnEdit = () => {
-   navigate("/edit");
+   navigate("/edit", { state: { id, title, text } });
  };

Notice how I send the “state” through navigation. This “state” is not the same as a React component state. This “state” belongs to the browser history API (see History.state). The state object can be anything that can be serialized (so you cannot pass functions to it, only data).

In order to retrieve the history state, we need to employ the useLocation hook from the react-router library.

Update the pages/Edit.jsx as follows:

import { Button, Container, Group, Stack } from "@mantine/core";
import { TextInput, Textarea } from "@mantine/core";
import { useEffect, useState } from "react";
import PropTypes from "prop-types";
import { useNavigate, useLocation } from "react-router-dom";

function Edit(props) {
  const { edit } = props;
  const navigate = useNavigate();
  const location = useLocation();
  const { id, text: _text, title: _title, mode } = location.state;

  const [title, setTitle] = useState("");
  const [text, setText] = useState("");

  useEffect(() => {
    if (_text !== text) {
      setText(_text);
    }

    if (_title !== title) {
      setTitle(_title);
    }
  }, [_text, _title]);

  const handleTitleChange = (event) => {
    setTitle(event.target.value);
  };

  const handleTextChange = (event) => {
    setText(event.target.value);
  };

  const handleSave = () => {
    edit(id, title, text);
    navigate("/", { replace: true });
  };

  const handleCancel = () => {
    console.log("cancel");
    navigate("/", { replace: true });
  };

  return (
    <Container>
      <Stack spacing="lg">
        <TextInput
          placeholder="Your note's title"
          label="Title"
          withAsterisk
          value={title}
          onChange={handleTitleChange}
        />
        <Textarea
          placeholder="Your note's text"
          label="Text"
          withAsterisk
          autosize
          minRows={5}
          value={text}
          onChange={handleTextChange}
        />
        <Group position="center" spacing="xl" grow>
          <Button variant="subtle" onClick={handleCancel}>
            Cancel
          </Button>
          <Button variant="default" onClick={handleSave}>
            Save
          </Button>
        </Group>
      </Stack>
    </Container>
  );
}

export default Edit;

Edit.propTypes = {
  edit: PropTypes.func,
}; 

Notice after retrieving the history state from the location.state object, I used a useEffect hook to initialize the title and text states.

Observe how the editing feature works now: