1

I'm new to React and I'm struggling to ensure that form elements created from a mapped array remain controlled by React

I have an array of objects that is passed into a React component through props

vehicles: [
    {make: 'make1', model: 'model1'},
    {make: 'make2', model: 'model2'}
]

I'm then storing these in state (the purpose of the React component in question is to amend the vehicles array at its source)

componentWillReceiveProps() {
    this.setState({
        vehicles: this.props.vehicles
    })
}

After this I'm mapping through the array to create jsx to be returned in render()

const vehicles = this.state.vehicles.map((vehicle, index) => {
    return (
        <div>
            <FormGroup row>
                <Col sm={1}></Col>
                <Label for={`make-${index}`} sm={3}>Make</Label>
                <Col sm={7}>
                    <Input type="text" name={`make-${index}`} id={`make-${index}`} data-context="vehicle" onChange={this.handleInputChange} value={vehicle.make} readOnly={!this.state.formEnabled} />
                </Col>
                <Col sm={1}></Col>
            </FormGroup>
            <FormGroup row>
                <Col sm={1}></Col>
                <Label for={`model-${index}`} sm={3}>Model</Label>
                <Col sm={7}>
                    <Input type="text" name={`model-${index}`} id={`model-${index}`} data-context="vehicle" onChange={this.handleInputChange} value={vehicle.model} readOnly={!this.state.formEnabled} />
                </Col>
                <Col sm={1}></Col>
            </FormGroup>
        </div>
    );
})

However, setting the values this way means that I can't change their state using the handleInputChange() function (or even enter text in the input fields in the browser).

Is there a way to keep dynamically created form elements controlled by React? Or is my approach fundamentally flawed?

1
  • 1
    you can change the state in handleInputChange() by calling setState inside your function Commented Feb 22, 2018 at 12:53

1 Answer 1

1

You can pass the index as a data attribute on the input, and then update the array in your handleInputChange function and update the state with the new array. Something like this:

handleInputChange(event) {
  const thisVehicleIndex = event.target.dataset.item
  let vehicles = this.state.vehicles
  let thisVehicle = vehicles[thisVehicleIndex]
  // Handle your update thisVehicle, then...
  vehicles[thisVehicleIndex] = thisVehicle
  this.setState({
    vehicles: vehicles
  })
}

Your input would look something like this (note the new data-item attribute):

<Input type="text" name={`make-${index}`} id={`make-${index}`} data-context="vehicle" data-item={index} onChange={this.handleInputChange} value={vehicle.make} readOnly={!this.state.formEnabled} />
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks Joe, just what I was after

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.