Reputation: 15107
I have a form that I am using to Create a dog. I would also like to use this same form to update/edit that dog but I'm running into issues where I can't my input form is not editable (I think some type of race condition of what is getting updated and when).
Here is my original form that worked as expected to create a Dog:
import React from 'react'
import PropTypes from 'prop-types'
class CreateDogForm extends React.Component {
state = {
name: '',
ageInYears: "0",
ageInMonths: "0",
gender: null,
neuteredOrSpayed: null,
weightInLbs: "0",
bodyCondition: null,
activityLevel: null,
breeds: [],
}
handleChange = (e) => {
console.log('value:', e.target.value)
console.log('options:', e.target.options)
console.log('type:', e.target.type)
let value = e.target.value
if(e.target.type === 'number') {
value = parseInt(e.target.value)
}
this.setState({[e.target.name]: value})
}
handleSubmit = (e) => {
e.preventDefault()
const dog = {
name: this.state.name,
ageInYears: this.state.ageInYears,
ageInMonths: this.state.ageInMonths,
gender: this.state.gender,
neuteredOrSpayed: this.state.neuteredOrSpayed,
weightInLbs: this.state.weightInLbs,
bodyCondition: this.state.bodyCondition,
activityLevel: this.state.activityLevel,
breeds: this.state.breeds,
createdAt: new Date(),
createdBy: "kamtodo"
}
if (dog.name) {
this.props.createNewDog(dog)
} else {
alert('Please fix all errors. ')
}
}
render() {
return (
<form className="create-dog-wrapper" onSubmit={this.handleSubmit}>
<div>
<label>Dog's Name:</label><input type="text" name="name" placeholder="Dog name" required onChange={this.handleChange}/>
</div>
<div>
<label>Age:</label><input type="number" name="ageInYears" placeholder="(Years)" min="0" max="30" onChange={this.handleChange}/> and
<input type="number" name="ageInMonths" placeholder="(Months)" min="0" max="11" onChange={this.handleChange}/>
</div>
<div>
<label>Gender:</label>
<select name="gender" defaultValue="" required onChange={this.handleChange}>
<option disabled value=""> -- select an option -- </option>
<option value="male">Male</option>
<option value="female">Female</option>
</select>
</div>
<div>
<label>Neutered/Spayed:</label>
<select name="neuteredOrSpayed" defaultValue="" required onChange={this.handleChange}>
<option disabled value=""> -- select an option -- </option>
<option value="yes">Yes</option>
<option value="no">No</option>
</select>
</div>
<div>
<label>Weight:</label><input type="number" name="weightInLbs" required placeholder="(Pounds)" min="0" max="343" onChange={this.handleChange}/>
</div>
<div>
<label>Body Condition:</label>
<select name="bodyCondition" defaultValue="" required onChange={this.handleChange}>
<option disabled value=""> -- select an option -- </option>
<option value="underweight">Underweight</option>
<option value="correct_weight">Correct Weight</option>
<option value="overweight">Overweight</option>
</select>
</div>
<div>
<label>Activity Level:</label>
<select name="activityLevel" defaultValue="" required onChange={this.handleChange}>
<option disabled value=""> -- select an option -- </option>
<option value="low_activity">Low Activity</option>
<option value="normal_activity">Normal Activity</option>
<option value="high_activity">High Activity</option>
</select>
</div>
<div>
<label>Dog's Breed:</label>
<select name="breeds" defaultValue={[]} required multiple>
<option value="1c3b58ee-2fc7-40ad-bde2-99361373f0ec">Dalmatian</option>
<option value="2ec3b80a-b1c4-4cc6-b0c5-f3d20e574eaa">Maltese</option>
<option value="8a1ca2e2-13fc-4c6d-9944-5b102b04d287">Poodle</option>
</select>
</div>
<div>
<button type="submit">Create New Dog</button>
</div>
</form>
)
}
}
CreateDogForm.propTypes = {
createNewDog: PropTypes.func.isRequired
}
export default CreateDogForm
Here is what my new form to CreateOrUpdate a Dog looks like. When I display the form on the web I am not able to edit any fields (probably some kind of circular dependency issue):
import React from 'react'
import PropTypes from 'prop-types'
class CreateOrUpdateDogForm extends React.Component {
state = {
id: null,
name: '',
ageInYears: "0",
ageInMonths: "0",
gender: null,
neuteredOrSpayed: null,
weightInLbs: "0",
bodyCondition: null,
activityLevel: null,
breeds: [],
}
handleChange = (e) => {
console.log('value:', e.target.value)
console.log('options:', e.target.options)
console.log('type:', e.target.type)
let value = e.target.value
if(e.target.type === 'number') {
value = parseInt(e.target.value)
}
this.setState({[e.target.name]: value})
}
handleSubmit = (e) => {
e.preventDefault()
const dog = {
id: this.state.id,
name: this.state.name,
ageInYears: this.state.ageInYears,
ageInMonths: this.state.ageInMonths,
gender: this.state.gender,
neuteredOrSpayed: this.state.neuteredOrSpayed,
weightInLbs: this.state.weightInLbs,
bodyCondition: this.state.bodyCondition,
activityLevel: this.state.activityLevel,
breeds: this.state.breeds,
createdAt: new Date(),
createdBy: "kamtodo"
}
// @kamtood: Perform full validation here
// @kamtood: Perform casting from string to int in appropriate fields
if (dog.name) {
this.props.createNewDog(dog)
} else {
alert('Please fix all errors. ')
}
}
render() {
return (
<form className="create-dog-wrapper" onSubmit={this.handleSubmit}>
<input type="text" name="id" value={this.props.dogId ||''} readOnly/>
<div>
<label>Dog's Name:</label><input type="text" name="name" onChange={this.handleChange} value={this.props.dogData.name ||''} placeholder="Dog name" required />
</div>
<div>
<label>Age:</label><input type="number" name="ageInYears" onChange={this.handleChange} value={this.props.dogData.ageInYears || ''} placeholder="(Years)" min="0" max="30" /> and
<input type="number" name="ageInMonths" onChange={this.handleChange} value={this.props.dogData.ageInMonths || ''} placeholder="(Months)" min="0" max="11" />
</div>
<div>
<label>Gender:</label>
<select name="gender" onChange={this.handleChange} value={this.props.dogData.gender ||''} required>
<option disabled value=""> -- select an option -- </option>
<option value="male">Male</option>
<option value="female">Female</option>
</select>
</div>
<div>
<label>Neutered/Spayed:</label>
<select name="neuteredOrSpayed" onChange={this.handleChange} value={this.props.dogData.neuteredOrSpayed ||''} required>
<option disabled value=""> -- select an option -- </option>
<option value="yes">Yes</option>
<option value="no">No</option>
</select>
</div>
<div>
<label>Weight:</label><input type="number" name="weightInLbs" onChange={this.handleChange} value={this.props.dogData.weightInLbs ||''} required placeholder="(Pounds)" min="0" max="343" />
</div>
<div>
<label>Body Condition:</label>
<select name="bodyCondition" onChange={this.handleChange} value={this.props.dogData.weightInLbs ||''} required>
<option disabled value=""> -- select an option -- </option>
<option value="underweight">Underweight</option>
<option value="correct_weight">Correct Weight</option>
<option value="overweight">Overweight</option>
</select>
</div>
<div>
<label>Activity Level:</label>
<select name="activityLevel" onChange={this.handleChange} value={this.props.dogData.activityLevel ||''} required>
<option disabled value=""> -- select an option -- </option>
<option value="low_activity">Low Activity</option>
<option value="normal_activity">Normal Activity</option>
<option value="high_activity">High Activity</option>
</select>
</div>
<div>
<label>Dog's Breed:</label>
<select name="breeds" defaultValue={[]} required multiple>
<option value="1c3b58ee-2fc7-40ad-bde2-99361373f0ec">Dalmatian</option>
<option value="2ec3b80a-b1c4-4cc6-b0c5-f3d20e574eaa">Maltese</option>
<option value="8a1ca2e2-13fc-4c6d-9944-5b102b04d287">Poodle</option>
</select>
</div>
<div>
<button type="submit">Save</button>
</div>
</form>
)
}
}
CreateOrUpdateDogForm.propTypes = {
createNewDog: PropTypes.func.isRequired,
dogId: PropTypes.string,
dogData: PropTypes.object.isRequired,
}
export default CreateOrUpdateDogForm
Upvotes: 0
Views: 3664
Reputation: 14610
Your component will only re-render when the props to it change or your internal state changes. Your form is using the props as values, and the props aren't changing when you type, so they stay the same.
You need to use your component's state for the input values. This is also known as controlled forms.
So instead of:
<div>
<label>Dog's Name:</label><input type="text" name="name" onChange={this.handleChange} value={this.props.dogData.name ||''} placeholder="Dog name" required />
</div>
do this:
<div>
<label>Dog's Name:</label><input type="text" name="name" onChange={this.handleChange} value={this.state.name ||''} placeholder="Dog name" required />
</div>
But of course for this to work you will need to map your incoming props to the component's state when it loads:
constructor(props) {
super(props);
this.state = {
xyx: props.dogData.xyz
...
}
}
Upvotes: 1