Reputation: 3259
What am I doing wrong here? The form below was working fine until I started wiring up state. If I breakpoint or log the code I can see that the values of 'this.state.data.xxx' are being changed on the event method calls but the data then empties from the textboxes immediately afterwards and isn't persisted. I can only think I'm doing something wrong somewhere.
export default class RegistrationForm extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {},
employersLookupData: []
};
this.employersDataSource = new LookupRestServiceGateway("/api/lookups/employers/");
this.handleEmailChange = this.handleEmailChange.bind(this);
this.handlePasswordChange = this.handlePasswordChange.bind(this);
this.handleConfirmPasswordChange = this.handleConfirmPasswordChange.bind(this);
this.handleFirstNameChange = this.handleFirstNameChange.bind(this);
this.handleLastNameChange = this.handleLastNameChange.bind(this);
this.handleEmployerChange = this.handleEmployerChange.bind(this);
}
handleEmailChange(e) {
console.log(this);
this.state.data.email = e.target.value;
}
handlePasswordChange(e) {
this.state.data.password = e.target.value;
}
handleConfirmPasswordChange(e) {
this.state.data.confirmPassword = e.target.value;
}
handleFirstNameChange(e) {
this.state.data.firstName = e.target.value;
}
handleLastNameChange(e) {
this.state.data.lastName = e.target.value;
}
handleEmployerChange(e) {
this.state.data.employerID = e.target.value;
}
loadLookupData() {
this.employersDataSource.getListItems({ successCallback: (data) => {
this.setState({ employersLookupData: data ? data.items : [] });
}});
}
componentDidMount() {
this.loadLookupData();
}
render() {
return (
<Form.Wrapper formId = "registration-form"
className = "form-horizontal"
onSubmit = {this.onSubmit}
onSubmitSuccess = {this.onSubmitSuccess}>
<h4>Create a new account.</h4>
<hr/>
<Form.Line name = "Email"
label = "Email"
type = "email"
inputClassName = "col-md-10 col-sm-9" labelClassName = "col-md-2 col-sm-3"
value = {this.state.data.email || ""}
onChange = {this.handleEmailChange} />
<Form.Line name = "Password"
label = "Password"
type = "password"
inputClassName = "col-md-10 col-sm-9" labelClassName = "col-md-2 col-sm-3"
value = {this.state.data.password || ""}
onChange = {this.handlePasswordChange} />
<Form.Line name = "ConfirmPassword"
label = "Confirm Password"
type = "password"
inputClassName = "col-md-10 col-sm-9" labelClassName = "col-md-2 col-sm-3"
value = {this.state.data.confirmPassword || ""}
onChange = {this.handleConfirmPasswordChange} />
<Form.Line name = "FirstName"
label = "First Name"
inputClassName = "col-md-10 col-sm-9" labelClassName = "col-md-2 col-sm-3"
value = {this.state.data.firstName || ""}
onChange = {this.handleFirstNameChange} />
<Form.Line name = "LastName"
label = "Last Name"
inputClassName = "col-md-10 col-sm-9" labelClassName = "col-md-2 col-sm-3"
value = {this.state.data.lastName || ""}
onChange = {this.handleLastNameChange} />
<Form.DropDownLine name = "EmployerID"
label = "Employer"
inputClassName = "col-md-10 col-sm-9" labelClassName = "col-md-2 col-sm-3"
emptySelection = "Please select an employer…"
onChange = {this.handleEmployerChange}
items = {this.state.data.employersLookupData}/>
<Form.Buttons.Wrapper className="col-sm-offset-3 col-md-offset-2 col-md-10 col-sm-9">
<Form.Buttons.Submit text = "Register"
icon = "fa-user-plus" />
</Form.Buttons.Wrapper>
</Form.Wrapper>
);
}
}
Just for completeness, Form.Line refers to the FormLine component below...
export default class FormLine extends React.Component {
render() {
let controlClassName = this.props.children
? `form-control ${this.props.controlClassName} noted-form-control`
: `form-control ${this.props.controlClassName}`;
return (
<div className="row padded-row">
<div className="form-group">
<FormLabel name = {this.props.name}
className = {this.props.labelClassName}
value = {this.props.label} />
<div className={this.props.inputClassName}>
<TextBox name = {this.props.name}
className = {controlClassName}
maxLength = {this.props.maxLength}
value = {this.props.value}
type = {this.props.type}
onChange = {this.props.onChange} />
<FormInputNotes>{this.props.children}</FormInputNotes>
</div>
</div>
</div>
);
}
}
and TextBox looks like this...
export default function TextBox({name = "", value = "", type = "text", className = "", maxLength = 10000, onChange}) {
return (
<input type = {type}
htmlFor = {name}
className = {`form-control ${className}`}
maxLength = {maxLength}
value = {value}
onChange = {onChange} />
);
}
Upvotes: 1
Views: 42
Reputation: 3866
I think your problem is that you are misunderstanding the way the state should be changed. You are doing this:
this.state.data.employerID = e.target.value;
and you should do this:
let newData = {
...this.state.data,
employerID: e.target.value
}
this.setState({data: newData});
Lets take an example:
Let start an initial state like this:
this.state = { someValue: 0 };
Doing this.state.someValue = 1
does not notify to React that the state has changed, so It will never render the canges.
Here is where this.setState
takes place, using it will notify to React Core that the state has changed and needs to be recalculated (and rendered after too). The correct way following with the example is:
this.setState({ someValue: 0 });
In your case, due you are using a complex object, you need to replace the full object, that's why you need to make a copy, change the value you need and update it using this.setState
.
Your handlers should look more or less like this:
handleFirstNameChange(e) {
//Create a copy of the original object
let newData = {
...this.state.data,
//Set the new value
employerID: e.target.value
}
//Set the new value through setState
this.setState({data: newData});
}
For reference or more information about React's Components read the full docs. If you have time, take a look at Components Cycle Life as well.
Let me know if it works.
Upvotes: 2