简体   繁体   中英

ReactJS Form: Parent Component state updates, but Child's field gets emptied & props remain unchanged

I've written ReactJS code for a dynamic form to collect student details. Any number of students can be added or deleted. Parent component is App & Child Component is StudentsFormElement which renders one student input field. Adding & deleting fields work perfectly fine.


Problem: I cannot type beyond one character in the form fields, and they clear up immediately after typing. Using DevTools, I can see that the character typed gets reflected in Parent Component's state but the change fails to propagate down through props to the Child Component. So, the DOM clears up to the original state. (NOTE: I found several questions which are similar, but none of them answer this particular problem). IGNORE the else condition in onStudentsChange() which is commented out. It deals with adding & deleting students.


codesandbox: https://codesandbox.io/embed/inspiring-kare-qb9hz

App (parent):

class App extends React.Component {
  constructor(props) {
    super(props)
    let studentsFormElementsTemp = []
    let tempSTUDENTS = {0: ["", ""]}
    this.state = {
      STUDENTS: tempSTUDENTS
    }
    studentsFormElementsTemp.push(<StudentsFormElement id="0" student={this.state.STUDENTS[0]} onStudentsChange={this.onStudentsChange} />)
    this.state = {
      studentsFormElements: studentsFormElementsTemp,
      studentsElementsIdArray: [0],
      STUDENTS: tempSTUDENTS
    } 
  }

  onStudentsChange = (e) => {
    if (e.target.name === "studentId" || e.target.name === "studentName") {
      let tempSTUDENTS = this.state.STUDENTS
      if (e.target.name === "studentId") {
        tempSTUDENTS[e.target.id][0] = e.target.value
      }
      else {
        tempSTUDENTS[e.target.id][1] = e.target.value
      }
      this.setState({
        STUDENTS: tempSTUDENTS
      })
    } else { //IGNORE. DEALS WITH 'X' and '+' BUTTONS.
      /*let studentsFormElementsTemp = this.state.studentsFormElements
      let studentsElementsIdArrayTemp = this.state.studentsElementsIdArray
      let tempSTUDENTS = this.state.STUDENTS
      if (e.target.id === "+") {
        tempSTUDENTS[studentsElementsIdArrayTemp[studentsElementsIdArrayTemp.length - 1] + 1] = ["", ""]
        this.setState({
          STUDENTS: tempSTUDENTS
        })
        studentsFormElementsTemp.push(<StudentsFormElement id={studentsElementsIdArrayTemp[studentsElementsIdArrayTemp.length - 1] + 1} student={this.state.STUDENTS[studentsElementsIdArrayTemp[studentsElementsIdArrayTemp.length - 1] + 1]} onStudentsChange={this.onStudentsChange} />)
        studentsElementsIdArrayTemp.push(studentsElementsIdArrayTemp[studentsElementsIdArrayTemp.length - 1] + 1)
        this.setState({
            studentsFormElements: studentsFormElementsTemp,
            studentsElementsIdArray: studentsElementsIdArrayTemp
        })
      } else {
        let studentIndex = studentsElementsIdArrayTemp.indexOf(parseInt(e.target.id))
        studentsFormElementsTemp.splice(studentIndex, 1)
        studentsElementsIdArrayTemp.splice(studentIndex, 1)
        delete tempSTUDENTS[e.target.id]
        this.setState({
            studentsFormElements: studentsFormElementsTemp,
            studentsElementsIdArray: studentsElementsIdArrayTemp,
            STUDENTS: tempSTUDENTS
        })
      }
    */}
  }

  render() {
    return (
        <div>
            <h2 style={{textAlign: "center", display: "inline-block"}}>Students</h2><Button id="+" style={{display: "inline-block"}} variant="success" onClick={this.onStudentsChange}>+</Button>
            <form>
                {this.state.studentsFormElements}
            </form>
        </div>
    )
  }
}

StudentsFormElement (child):

class StudentsFormElement extends React.Component {
  render() {
    return (
      <InputGroup className="mb-3">
        <FormControl name="studentId" id={this.props.id} value={this.props.student[0]} placeholder="Id" onChange={this.props.onStudentsChange} />
        <FormControl name="studentName" id={this.props.id} value={this.props.student[1]} placeholder="Name" onChange={this.props.onStudentsChange} />
        <InputGroup.Append style={{display: "inline-block"}}>
          <Button id={this.props.id} variant="danger" onClick={this.props.onStudentsChange}>X</Button>
        </InputGroup.Append>
      </InputGroup>
    )
  }
}

Here's a version with just one student field with no add/delete buttons. Since I used the Child Component directly in the render() of the Parent Component, typing works. But I need to make it work along with multiple student fields like above code. https://codesandbox.io/s/quirky-colden-gkiz6

At first, the value might be undefined in the FormControl field because this.props.student[0] is undefined Remove value property in FormControl And assign the props value in defaultValue property

<FormControl name="studentId" id={this.props.id} defaultValue={this.props.student[0]} placeholder="Id" onChange={this.props.onStudentsChange} />
<FormControl name="studentName" id={this.props.id} defaultValue={this.props.student[1]} placeholder="Name" onChange={this.props.onStudentsChange} />

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM