Reputation: 483
I have 2 <select>
lists in my React.js application. They render fine, and are attached to a handleChange
function. They are programmed to initially default to the first option.
When the user selects another option, the list visually seems to maintain the first option. However, when I submit the form and alert out the associated values in the state, it alerts the values selected by the user.
Here's my render function:
<div id="userActions">
<form id="userActionsForm" onSubmit={this.handleSubmit}>
<label htmlFor="operator">Operator:</label>
<select
id="operator"
name="operator"
value={this.state.operator}
onChange={this.handleChange}
>
{this.state &&
this.state.operators &&
this.state.operators.map((op) => <option value={op}>{op}</option>)}
</select>
<label htmlFor="user">User:</label>
<select
id="user"
name="user"
value={this.state.user}
onChange={this.handleChange}
>
{this.state &&
this.state.testUsers &&
this.state.testUsers.map((tu) => <option value={tu}>{tu}</option>)}
</select>
<div className="submit">
<input type="submit" value="Submit" />
</div>
<span className="error">{this.state.error}</span>
</form>
</div>
Here's my onChange
function:
handleChange(event) {
this.state.value = event.currentTarget.value;
if (event.currentTarget.id === 'operator') {
this.state.operator = event.currentTarget.value;
} else if (event.currentTarget.id === 'user') {
this.state.user = event.currentTarget.value;
}
}
Here's my onSubmit
:
handleSubmit(event) {
alert(this.state.user);
alert(this.state.operator);
event.preventDefault();
}
Upvotes: 1
Views: 74
Reputation: 23189
The problem is that you're mutating the state in handleChange
by updating it like this this.state.operator = event.currentTarget.value;
. You should never ever update the state directly. You need to use setState
.
I've just created a snippet for you below. You can click on "Run code snippet" and see it in action. I made the handleChange
method simpler by using event.target.name
as the key in the state
object.
You also need to add a unique key
prop for each child in a list (e.g. when you use .map()
).
const operators = ["op1", "op2", "op3"];
const testUsers = ["user1", "user2", "user3", "user4"];
class App extends React.Component {
state = {
operator: operators[0],
operators,
testUsers,
user: testUsers[0],
};
handleChange = (event) => {
this.setState({
[event.target.name]: event.target.value
});
};
handleSubmit = (event) => {
event.preventDefault();
console.log({
user: this.state.user,
operator: this.state.operator,
});
};
render() {
return (
<form id="userActionsForm" onSubmit={this.handleSubmit}>
<label htmlFor="operator">Operator:</label>
<select
id="operator"
name="operator"
value={this.state.operator}
onChange={this.handleChange}
>
{this.state.operators.map((operator) => (
<option key={operator} value={operator}>
{operator}
</option>
))}
</select>
<label htmlFor="user">User:</label>
<select
id="user"
name="user"
value={this.state.user}
onChange={this.handleChange}
>
{this.state.testUsers.map((user) => (
<option key={user} value={user}>
{user}
</option>
))}
</select>
<div className="submit">
<button type="submit">Submit</button>
</div>
<span className="error">{this.state.error}</span>
</form>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Upvotes: 1