10. Use Forms To Add An Item

10.1. Add The Form

To be able to add FAQ items to the list we will start by adding an add form:

31render() {
32  return (
33    <div>
34      <ul>
35        {this.state.faq.map((item, index) => (
36          <FaqItem
37            question={item.question}
38            answer={item.answer}
39            index={index}
40            onDelete={this.onDelete}
41          />
42        ))}
43      </ul>
44      <form>
45        <label>
46          Question: <input name="question" type="text" />
47        </label>
48        <label>
49          Answer: <textarea name="answer" />
50        </label>
51        <input type="submit" value="Add" />
52      </form>
53    </div>
54  );
55}

Differences

--- a/src/App.js
+++ b/src/App.js
@@ -30,16 +30,27 @@ class App extends Component {

  render() {
    return (
-      <ul>
-        {this.state.faq.map((item, index) => (
-          <FaqItem
-            question={item.question}
-            answer={item.answer}
-            index={index}
-            onDelete={this.onDelete}
-          />
-        ))}
-      </ul>
+      <div>
+        <ul>
+          {this.state.faq.map((item, index) => (
+            <FaqItem
+              question={item.question}
+              answer={item.answer}
+              index={index}
+              onDelete={this.onDelete}
+            />
+          ))}
+        </ul>
+        <form>
+          <label>
+            Question: <input name="question" type="text" />
+          </label>
+          <label>
+            Answer: <textarea name="answer" />
+          </label>
+          <input type="submit" value="Add" />
+        </form>
+      </div>
    );
  }
}

10.2. Manage Field Values In The State

To manage the values of the fields in the form we will use the state. Add a question and answer value to the state which contains the values of the inputs. Add onChange handlers to the input and textarea which will change the values in the state when the input changes. This pattern is called controlled inputs.

Solution

 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  onChangeQuestion = (event) => {
35    this.setState({
36      question: event.target.value
37    });
38  }
39
40  onChangeAnswer = (event) => {
41    this.setState({
42      answer: event.target.value
43    });
44  }
45
46  render() {
47    return (
48      <div>
49        <ul>
50          {this.state.faq.map((item, index) => (
51            <FaqItem
52              question={item.question}
53              answer={item.answer}
54              index={index}
55              onDelete={this.onDelete}
56            />
57          ))}
58        </ul>
59        <form>
60          <label>
61            Question:
62            <input
63              name="question"
64              type="text"
65              value={this.state.question}
66              onChange={this.onChangeQuestion}
67            />
68          </label>
69          <label>
70            Answer:
71            <textarea
72              name="answer"
73              value={this.state.answer}
74              onChange={this.onChangeAnswer}
75            />
76          </label>
77          <input type="submit" value="Add" />
78        </form>
79      </div>
80    );
81  }
82}
83
84export default App;
--- a/src/App.js
+++ b/src/App.js
@@ -6,17 +6,23 @@ class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      faq: [
        {
          question: "What does the Plone Foundation do?",
-          answer: "The mission of the Plone Foundation is to protect and..."
+          answer:
+            "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."
        },
        {
          question: "Why does Plone need a Foundation?",
-          answer: "Plone has reached critical mass, with enterprise..."
+          answer:
+            "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."
        }
-      ]
+      ],
+      question: "",
+      answer: ""
    };
  }

@@ -28,6 +34,18 @@ class App extends Component {
    });
  }

+  onChangeQuestion = (event) => {
+    this.setState({
+      question: event.target.value
+    });
+  }
+
+  onChangeAnswer = (event) => {
+    this.setState({
+      answer: event.target.value
+    });
+  }
+
  render() {
    return (
      <div>
@@ -43,10 +61,21 @@ class App extends Component {
        </ul>
        <form>
          <label>
-            Question: <input name="question" type="text" />
+            Question:
+            <input
+              name="question"
+              type="text"
+              value={this.state.question}
+              onChange={this.onChangeQuestion}
+            />
          </label>
          <label>
-            Answer: <textarea name="answer" />
+            Answer:
+            <textarea
+              name="answer"
+              value={this.state.answer}
+              onChange={this.onChangeAnswer}
+            />
          </label>
          <input type="submit" value="Add" />
        </form>

10.3. Submit Handler

Now that our values are managed in the state we can write our submit handler. Write an onSubmit handler which reads the values from the state and add the new FAQ item to the list. After the item is added the inputs should also reset to empty values.

Solution

Make sure you bind the onSubmit handler.

11

And add this to the body of the class.

 50onSubmit = (event) => {
 51  this.setState({
 52    faq: [
 53      ...this.state.faq,
 54      {
 55        question: this.state.question,
 56        answer: this.state.answer
 57      }
 58    ],
 59    question: "",
 60    answer: ""
 61  });
 62  event.preventDefault();
 63}
 64
 65render() {
 66  return (
 67    <div>
 68      <ul>
 69        {this.state.faq.map((item, index) => (
 70          <FaqItem
 71            question={item.question}
 72            answer={item.answer}
 73            index={index}
 74            onDelete={this.onDelete}
 75          />
 76        ))}
 77      </ul>
 78      <form onSubmit={this.onSubmit}>
 79        <label>
 80          Question:
 81          <input
 82            name="question"
 83            type="text"
 84            value={this.state.question}
 85            onChange={this.onChangeQuestion}
 86          />
 87        </label>
 88        <label>
 89          Answer:
 90          <textarea
 91            name="answer"
 92            value={this.state.answer}
 93            onChange={this.onChangeAnswer}
 94          />
 95        </label>
 96        <input type="submit" value="Add" />
 97      </form>
 98    </div>
 99  );
100}
--- a/src/App.js
+++ b/src/App.js
@@ -8,6 +8,7 @@ class App extends Component {
    this.state = {
      faq: [
        {
@@ -46,6 +47,21 @@ class App extends Component {
    });
  }

+  onSubmit = (event) => {
+    this.setState({
+      faq: [
+        ...this.state.faq,
+        {
+          question: this.state.question,
+          answer: this.state.answer
+        }
+      ],
+      question: "",
+      answer: ""
+    });
+    event.preventDefault();
+  }
+
  render() {
    return (
      <div>
@@ -59,7 +75,7 @@ class App extends Component {
            />
          ))}
        </ul>
-        <form>
+        <form onSubmit={this.onSubmit}>
          <label>
            Question:
            <input