11. Use Initial Form Data To Edit An Item

11.1. Two Modes For The FAQ Item

We will use inline editing to edit an item. Create a button to switch to 'edit' mode. This mode should be set in the state. Change the render method to show a form (similar to the 'add' form) in 'edit' mode and the view we currently have in the 'view' mode. The onSave handler can be a dummy handler for now, first we will focus on the two modes.

Solution

 1import React, { Component } from "react";
 2import PropTypes from "prop-types";
 3import "./FaqItem.css";
 4
 5class FaqItem extends Component {
 6  static propTypes = {
 7    question: PropTypes.string.isRequired,
 8    answer: PropTypes.string.isRequired,
 9    index: PropTypes.number.isRequired,
10    onDelete: PropTypes.func.isRequired
11  };
12
13  constructor(props) {
14    super(props);
15    this.state = {
16      show: false,
17      mode: "view"
18    };
19  }
20
21  toggle = () => {
22    this.setState({
23      show: !this.state.show
24    });
25  }
26
27  onDelete = () => {
28    this.props.onDelete(this.props.index);
29  }
30
31  onEdit = () => {
32    this.setState({
33      mode: "edit"
34    });
35  }
36
37  onSave = (event) => {
38    this.setState({
39      mode: "view"
40    });
41    event.preventDefault();
42  }
43
44  render() {
45    return this.state.mode === "edit" ? (
46      <li className="faq-item">
47        <form onSubmit={this.onSave}>
48          <label>
49            Question:
50            <input name="question" />
51          </label>
52          <label>
53            Answer:
54            <textarea name="answer" />
55          </label>
56          <input type="submit" value="Save" />
57        </form>
58      </li>
59    ) : (
60      <li className="faq-item">
61        <h2 onClick={this.toggle} className="question">
62          {this.props.question}
63        </h2>
64        {this.state.show && <p>{this.props.answer}</p>}
65        <button onClick={this.onDelete}>Delete</button>
66        <button onClick={this.onEdit}>Edit</button>
67      </li>
68    );
69  }
70}
71
72export default FaqItem;
--- a/src/components/FaqItem.jsx
+++ b/src/components/FaqItem.jsx
@@ -14,8 +14,11 @@ class FaqItem extends Component {
    super(props);
    this.state = {
-      show: false
+      show: false,
+      mode: "view"
    };
  }

@@ -29,14 +32,42 @@ class FaqItem extends Component {
    this.props.onDelete(this.props.index);
  }

+  onEdit = () => {
+    this.setState({
+      mode: "edit"
+    });
+  }
+
+  onSave = (event) => {
+    this.setState({
+      mode: "view"
+    });
+    event.preventDefault();
+  }
+
  render() {
-    return (
+    return this.state.mode === "edit" ? (
+      <li className="faq-item">
+        <form onSubmit={this.onSave}>
+          <label>
+            Question:
+            <input name="question" />
+          </label>
+          <label>
+            Answer:
+            <textarea name="answer" />
+          </label>
+          <input type="submit" value="Save" />
+        </form>
+      </li>
+    ) : (
      <li className="faq-item">
        <h2 onClick={this.toggle} className="question">
          {this.props.question}
        </h2>
        {this.state.show && <p>{this.props.answer}</p>}
        <button onClick={this.onDelete}>Delete</button>
+        <button onClick={this.onEdit}>Edit</button>
      </li>
    );
  }

11.2. Wiring Everything Together

Create a controlled form like the add form and pass an onEdit handler to the FaqItem component like we did with the onDelete

FaqItem.jsx

 1import React, { Component } from "react";
 2import PropTypes from "prop-types";
 3import "./FaqItem.css";
 4
 5class FaqItem extends Component {
 6  static propTypes = {
 7    question: PropTypes.string.isRequired,
 8    answer: PropTypes.string.isRequired,
 9    index: PropTypes.number.isRequired,
10    onDelete: PropTypes.func.isRequired,
11    onEdit: PropTypes.func.isRequired
12  };
13
14  constructor(props) {
15    super(props);
16    this.state = {
17      show: false,
18      mode: "view",
19      question: "",
20      answer: ""
21    };
22  }
23
24  toggle = () => {
25    this.setState({
26      show: !this.state.show
27    });
28  }
29
30  onDelete = () => {
31    this.props.onDelete(this.props.index);
32  }
33
34  onEdit = () => {
35    this.setState({
36      mode: "edit",
37      question: this.props.question,
38      answer: this.props.answer
39    });
40  }
41
42  onChangeQuestion = (event) => {
43    this.setState({
44      question: event.target.value
45    });
46  }
47
48  onChangeAnswer = (event) => {
49    this.setState({
50      answer: event.target.value
51    });
52  }
53
54  onSave = (event) => {
55    this.setState({
56      mode: "view"
57    });
58    this.props.onEdit(this.props.index, this.state.question, this.state.answer);
59    event.preventDefault();
60  }
61
62  render() {
63    return this.state.mode === "edit" ? (
64      <li className="faq-item">
65        <form onSubmit={this.onSave}>
66          <label>
67            Question:
68            <input name="question" value={this.state.question} onChange={this.onChangeQuestion} />
69          </label>
70          <label>
71            Answer:
72            <textarea name="answer" value={this.state.answer} onChange={this.onChangeAnswer} />
73          </label>
74          <input type="submit" value="Save" />
75        </form>
76      </li>
77    ) : (
78      <li className="faq-item">
79        <h2 onClick={this.toggle} className="question">
80          {this.props.question}
81        </h2>
82        {this.state.show && <p>{this.props.answer}</p>}
83        <button onClick={this.onDelete}>Delete</button>
84        <button onClick={this.onEdit}>Edit</button>
85      </li>
86    );
87  }
88}
89
90export default FaqItem;
--- a/src/components/FaqItem.jsx
+++ b/src/components/FaqItem.jsx
@@ -7,7 +7,8 @@ class FaqItem extends Component {
    question: PropTypes.string.isRequired,
    answer: PropTypes.string.isRequired,
    index: PropTypes.number.isRequired,
-    onDelete: PropTypes.func.isRequired
+    onDelete: PropTypes.func.isRequired,
+    onEdit: PropTypes.func.isRequired
  };

  constructor(props) {
@@ -15,10 +16,14 @@ class FaqItem extends Component {
    this.state = {
      show: false,
-      mode: "view"
+      mode: "view",
+      question: "",
+      answer: ""
    };
  }

@@ -34,7 +39,21 @@ class FaqItem extends Component {

  onEdit = () => {
    this.setState({
-      mode: "edit"
+      mode: "edit",
+      question: this.props.question,
+      answer: this.props.answer
+    });
+  }
+
+  onChangeQuestion = (event) => {
+    this.setState({
+      question: event.target.value
+    });
+  }
+
+  onChangeAnswer = (event) => {
+    this.setState({
+      answer: event.target.value
    });
  }

@@ -42,6 +61,7 @@ class FaqItem extends Component {
    this.setState({
      mode: "view"
    });
+    this.props.onEdit(this.props.index, this.state.question, this.state.answer);
    event.preventDefault();
  }

@@ -51,11 +71,19 @@ class FaqItem extends Component {
        <form onSubmit={this.onSave}>
          <label>
            Question:
-            <input name="question" />
+            <input
+              name="question"
+              value={this.state.question}
+              onChange={this.onChangeQuestion}
+            />
          </label>
          <label>
            Answer:
-            <textarea name="answer" />
+            <textarea
+              name="answer"
+              value={this.state.answer}
+              onChange={this.onChangeAnswer}
+            />
          </label>
          <input type="submit" value="Save" />
        </form>

App.js

  1import React, { Component } from "react";
  2import FaqItem from "./components/FaqItem";
  3import "./App.css";
  4
  5class App extends Component {
  6  constructor(props) {
  7    super(props);
  8    this.state = {
  9      faq: [
 10        {
 11          question: "What does the Plone Foundation do?",
 12          answer:
 13            "The mission of the Plone Foundation is to protect and promote Plone. The Foundation provides marketing assistance, awareness, and evangelism assistance to the Plone community. The Foundation also assists with development funding and coordination of funding for large feature implementations. In this way, our role is similar to the role of the Apache Software Foundation and its relationship with the Apache Project."
 14        },
 15        {
 16          question: "Why does Plone need a Foundation?",
 17          answer:
 18            "Plone has reached critical mass, with enterprise implementations and worldwide usage. The Foundation is able to speak for Plone, and provide strong and consistent advocacy for both the project and the community. The Plone Foundation also helps ensure a level playing field, to preserve what is good about Plone as new participants arrive."
 19        }
 20      ],
 21      question: "",
 22      answer: ""
 23    };
 24  }
 25
 26  onDelete = (index) => {
 27    let faq = this.state.faq;
 28    faq.splice(index, 1);
 29    this.setState({
 30      faq
 31    });
 32  }
 33
 34  onEdit = (index, question, answer) => {
 35    let faq = this.state.faq;
 36    faq[index] = {
 37      question,
 38      answer
 39    };
 40    this.setState({
 41      faq
 42    });
 43  }
 44
 45  onChangeQuestion = (event) => {
 46    this.setState({
 47      question: event.target.value
 48    });
 49  }
 50
 51  onChangeAnswer = (event) => {
 52    this.setState({
 53      answer: event.target.value
 54    });
 55  }
 56
 57  onSubmit = (event) => {
 58    this.setState({
 59      faq: [
 60        ...this.state.faq,
 61        {
 62          question: this.state.question,
 63          answer: this.state.answer
 64        }
 65      ],
 66      question: "",
 67      answer: ""
 68    });
 69    event.preventDefault();
 70  }
 71
 72  render() {
 73    return (
 74      <div>
 75        <ul>
 76          {this.state.faq.map((item, index) => (
 77            <FaqItem
 78              question={item.question}
 79              answer={item.answer}
 80              index={index}
 81              onDelete={this.onDelete}
 82              onEdit={this.onEdit}
 83            />
 84          ))}
 85        </ul>
 86        <form onSubmit={this.onSubmit}>
 87          <label>
 88            Question:
 89            <input
 90              name="question"
 91              type="text"
 92              value={this.state.question}
 93              onChange={this.onChangeQuestion}
 94            />
 95          </label>
 96          <label>
 97            Answer:
 98            <textarea
 99              name="answer"
100              value={this.state.answer}
101              onChange={this.onChangeAnswer}
102            />
103          </label>
104          <input type="submit" value="Add" />
105        </form>
106      </div>
107    );
108  }
109}
110
111export default App;
--- a/src/App.js
+++ b/src/App.js
@@ -6,6 +6,7 @@ class App extends Component {
  constructor(props) {
    super(props);
@@ -35,6 +36,17 @@ class App extends Component {
    });
  }

+  onEdit = (index, question, answer) => {
+    let faq = this.state.faq;
+    faq[index] = {
+      question,
+      answer
+    };
+    this.setState({
+      faq
+    });
+  }
+
  onChangeQuestion = (event) => {
    this.setState({
      question: event.target.value
@@ -72,6 +84,7 @@ class App extends Component {
              answer={item.answer}
              index={index}
              onDelete={this.onDelete}
+              onEdit={this.onEdit}
            />
          ))}
        </ul>