--- templateEngineOverride: false ---
Dr. Greg Bernstein
Updated April 20th, 2021
this<input> need a closing </input>class attributes must be changed to className similarly for other attribute namesFrom index.jsx in branch BasicForm1
class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {};
    }
render() {
    return (
      <main>
        <header>
          <h1>Membership</h1>
        </header>
        <h2>Apply Now!</h2>
        <section id="Application">
          <label>Name:</label>{" "}
          <input
            required
            minLength="1"
            maxLength="30"
            id="Name"
            type="text"
          ></input>
          <label>email:</label>
          <input required maxLength="50" id="Email" type="email"></input>
          <label>Password:</label>
          <input
            required
            type="password"
            minLength="8"
            maxLength="20"
          ></input>
          <label>Confirm Password:</label>
          <input
            required
            type="password"
            minLength="8"
            maxLength="20"
          ></input>
          <label>Level:</label>
          <select required>
            <option>Never Done It</option>
            <option>Beginner</option>
            <option>Intermediate</option>
            <option>Foils to TI and Back</option>
            <option>Racer</option>
          </select>
          <label>Comments:</label>
          <textarea name="comments" rows="8" cols="20" id="Comments"></textarea>
          <button id="Apply">Sign me up!</button>
        </section>
        <section id="ThanksDialog">
          <div className="message">
            <h3>Thanks for Signing Up</h3>
            <p id="UserInfo"></p>
            <button id="Close">Close</button>
          </div>
        </section>
      </main>
    );
    }
}
value attribute to each widget and “link it” to appropriate stateonInput, onChange or other handler as appropriate (I used arrow functions since these were so small)From index.jsx in branch BasicForm2
class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name: "", email: "", password: "", confPassword: "", level: "Beginner", comments: ""};
    }
render() {
    return (
      <main>
        <header>
          <h1>Membership</h1>
        </header>
        <h2>Apply Now!</h2>
        <section id="Application">
          <label>Name:</label>{" "}
          <input
            required
            minLength="1"
            maxLength="30"
            id="Name"
            type="text"
            value={this.state.name}
            onInput={(event) => this.setState({ name: event.target.value })}
          ></input>
          <label>email:</label>
          <input
            required
            maxLength="50"
            id="Email"
            type="email"
            value={this.state.email}
            onInput={(event) => this.setState({ email: event.target.value })}
          ></input>
          <label>Password:</label>
          <input
            required
            type="password"
            minLength="8"
            maxLength="20"
            value={this.state.password}
            onInput={(event) => this.setState({ password: event.target.value })}
          ></input>
          <label>Confirm Password:</label>
          <input
            required
            type="password"
            minLength="8"
            maxLength="20"
            value={this.state.confPassword}
            onInput={(event) =>
              this.setState({ confPassword: event.target.value })
            }
          ></input>
          <label>Level:</label>
          <select
            required
            value={this.state.level}
            onInput={(event) => this.setState({ level: event.target.value })}
          >
            <option>Never Done It</option>
            <option>Beginner</option>
            <option>Intermediate</option>
            <option>Foils to TI and Back</option>
            <option>Racer</option>
          </select>
          <label>Comments:</label>
          <textarea
            name="comments"
            rows="8"
            cols="20"
            value={this.state.comments}
            onInput={(event) => this.setState({ comments: event.target.value })}
          ></textarea>
          <button id="Apply">Sign me up!</button>
        </section>
        <section id="ThanksDialog">
          <div className="message">
            <h3>Thanks for Signing Up</h3>
            <p id="UserInfo"></p>
            <button id="Close">Close</button>
          </div>
        </section>
      </main>
    );
    }
}
<section>From index.jsx in branch BasicForm3
class App extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name: "", email: "", password: "", confPassword: "", level: "Beginner", 
        comments: "",
    dialogClass: ""};
    }
submitApplication() {
    // In a real application we'd actually send data to a server here
    // But all we'll do here is show the welcome/thanks dialog
    this.setState({ dialogClass: "show" })
}
render() {
    // Check if password and confirmation passwords match here
    let message = null;
    if (this.state.password.length < 8 || this.state.password !== this.state.confPassword) {
        message = <p>Password too short or not confirmed.</p>
    } else { // Everything is good create a welcome message
        message = <p>Welcome <em>{this.state.name}</em>,{" "}
        your email is <em>{this.state.email}</em>,{" "}
        your level is <em>{this.state.level}</em>{" "}
        and you had the following comments: <em>{this.state.comments}</em></p>
    }
    return (
      <main>
        <header>
          <h1>Membership</h1>
        </header>
        <h2>Apply Now!</h2>
        <section id="Application">
          <label>Name:</label>{" "}
          <input
            required
            minLength="1"
            maxLength="30"
            id="Name"
            type="text"
            value={this.state.name}
            onInput={(event) => this.setState({ name: event.target.value })}
          ></input>
          <label>email:</label>
          <input
            required
            maxLength="50"
            id="Email"
            type="email"
            value={this.state.email}
            onInput={(event) => this.setState({ email: event.target.value })}
          ></input>
          <label>Password:</label>
          <input
            required
            type="password"
            minLength="8"
            maxLength="20"
            value={this.state.password}
            onInput={(event) => this.setState({ password: event.target.value })}
          ></input>
          <label>Confirm Password:</label>
          <input
            required
            type="password"
            minLength="8"
            maxLength="20"
            value={this.state.confPassword}
            onInput={(event) =>
              this.setState({ confPassword: event.target.value })
            }
          ></input>
          <label>Level:</label>
          <select
            required
            value={this.state.level}
            onInput={(event) => this.setState({ level: event.target.value })}
          >
            <option>Never Done It</option>
            <option>Beginner</option>
            <option>Intermediate</option>
            <option>Foils to TI and Back</option>
            <option>Racer</option>
          </select>
          <label>Comments:</label>
          <textarea
            name="comments"
            rows="8"
            cols="20"
            value={this.state.comments}
            onInput={(event) => this.setState({ comments: event.target.value })}
          ></textarea>
          <button onClick={this.submitApplication.bind(this)}>
            Sign me up!
          </button>
        </section>
        <section id="ThanksDialog" className={this.state.dialogClass}>
          <div className="message">
            <h3>Thanks for Signing Up</h3>
            {message}
            <button onClick={(event) => this.setState({ dialogClass: "" })}>
              Close
            </button>
          </div>
        </section>
      </main>
    );
    }
}
Will create a new component to add/edit questions to Quiz-O-Matic
Need to take in text for the question, each of the choices and indicate the correct choice.
Example branch classComp4
Form component quickly become complicated!
<textarea> for question<input> for each choice<select> for choosing the answerKeep all the form info in the component state:
class AddQuestion extends React.Component {
  constructor(props) {
    super(props); // Must call
    this.state = {
      question: "When can you store plaintext passwords on the server?",
        choices: ["If I feel like it", "sometimes", "never"], answer: 2};
  }
  render() {...}
}
No “functionality” yet since no event handling:
render() { //familiar pattern below!
    let choiceItems = this.state.choices.map(function(choice, i) {
        return <li  key={"choice"+i}>
            <button>Delete</button><span>   </span>
            <input value={choice}/>
        </li>
    })
    const letters = "abcdefghi";
    let options = this.state.choices.map(function(choice, i){
        return <option key={"opt"+i} value={i}>{letters[i]+". "}</option>;
    }) //familiar pattern here too!
  return <div className="addQComp">
      <p>Question</p>
      <textarea value={this.state.question}></textarea>
      <p>Choices</p>
      <ol style={{listStyleType: "lower-alpha", paddingLeft: "1em"}}>
      {choiceItems}</ol>
        <p><span>Answer:</span>
        <select value={this.state.answer}>{options}</select></p>
      <p><button>Add Choice</button><button>Add Question</button></p>
  </div>;
}
Branch classComp5
textAreachange(event){
  this.setState({question: event.currentTarget.value});
}
onChange attribute to the text area:<textarea value={this.state.question} onChange={this.textAreachange.bind(this)}/>
this, Arrays, and bind()Issues:
Event Handler for choice <input> change:
choiceChange(i, event) {
  // Creates a new modified array of choices
  let newChoices = this.state.choices.map(function(choice, index){
    if (index === i) { // Only changes a particular choice
      return event.currentTarget.value; // from the <input> element
    } else
      return choice;
  })
  this.setState({choices: newChoices}); // update state
}
The following from render doesn’t work for multiple reasons!
let choiceItems = this.state.choices.map(function(choice, i) {
    return <li  key={"choice"+i}>
        <input onChange={this.choiceChange(i).bind(this)} value={choice}/>
    </li>
});
In a different function context!
Put this in a variable with a different name for safekeeping. Also see proper use of bind with additional variable i.
let that = this;  // Saves this into another variable for use below
let choiceItems = this.state.choices.map(function(choice, i) {
    return <li  key={"choice"+i}>
        <button onClick={that.delChoice.bind(that, i)}>Delete</button><span>   </span>
        <input onChange={that.choiceChange.bind(that, i)} value={choice}/>
    </li>
});
JSX for add choice button, and add question button:
<p><button onClick={this.addChoice.bind(this)}>Add Choice</button>
  <button onClick={this.addQuestion.bind(this)}>Add Question</button></p>
Event handling code, note use of array method:
addChoice() {
  // concat method create a new array with added element
  this.setState({choices: this.state.choices.concat("")});
}
JSX for delete choice buttons, note bind with i:
let choiceItems = this.state.choices.map(function(choice, i) {
    return <li  key={"choice"+i}>
        <button onClick={that.delChoice.bind(that, i)}>Delete</button><span>   </span>
        <input onChange={that.choiceChange.bind(that, i)} value={choice}/>
    </li>
});
Event handling code, note use of array filter method:
delChoice(i) {
  // filter produces a new array
  let upChoices = this.state.choices.filter(function(choice, index){
    if(index === i)
      return false;
    else
      return true;
  })
  this.setState({choices: upChoices});
}
AddQuestion component to be able to add a question to the app.questions array is part of its parents state so it can’t modify itquestions.addQuestion() on Quiz-o-Maticclass QuizOMatic extends React.Component {
  constructor(props) {
    super(props); // Must call
    // a member variable called "state" to hold the state as a JS object
    this.state = {
      show: "addQ", questions: questions,
      user: "CS351", score: "0/0", minutes: 12
    };
  }
  addQuestion(q) { // Take a multiple choice question as input
    this.setState({questions: this.state.questions.concat(q)});
    // Show the quiz so we can see it
    this.setState({show: "quiz"});
  }
  // other stuff
  render(){...}
}
addQuestion() downPart of render():
switch (this.state.show) {
  case "intro":
    contents = <Intro user={this.state.user} takeQuiz={this.quizHandler.bind(this)}/>;
    break;
  case "quiz":
    contents = <Quiz questions={this.state.questions} gradeIt={this.resultHandler.bind(this)}/>;
    break;
  case "summary":
    contents = <Summary user={this.state.user} score={this.state.score}
                        minutes={this.state.minutes} again={this.quizHandler.bind(this)}/>;
    break;
  case "addQ": // Passing addQuestion method down here.
    contents = <AddQuestion addQuestion={this.addQuestion.bind(this)}/>;
    break;
  default:
    contents = <h2>Warning something went wrong!!!</h2>;
}
AddQuestion component event handler:
addQuestion() { //Too many functions with the same name?
  // Put local state into nicely formatted object
  let q = {question: this.state.question, choices: this.state.choices,
    answer: this.state.answer};
  // Call passed down function with new question object
  this.props.addQuestion(q);
}
Portion of AddQuestion render:
<p><button onClick={this.addChoice.bind(this)}>Add Choice</button>
  <button onClick={this.addQuestion.bind(this)}>Add Question</button></p>