我正在寻找更新一个或多个字段的最佳方法,使用基于event.target.name的泛型函数,而不会改变状态。
示例:
onInputChange = event => {
const newEvent = this.state.event
newEvent[event.target.name] = event.target.value
this.setState({
event: newEvent
})
}
上述功能可以正常工作。然而,它正在改变setState方法之前的状态,因为newEvent是一个引用。
使用spread运算符复制状态不能按预期工作。 我缺少什么?
请参阅下面的完整代码和更深入的分析。
import React, { Component } from 'react'
class EventForm extends Component {
state = {
event: {
title: '',
venue: ''
}
}
onFormSubmit = event => {
event.preventDefault()
//..
}
onInputChange = event => {
const newEvent = this.state.event
newEvent[event.target.name] = event.target.value
this.setState({
event: newEvent
})
}
render() {
const { event } = this.state
return (
<div>
<div>
Title: {this.state.event.title}
<br />
Venue: {this.state.event.venue}
</div>
<form onSubmit={this.onFormSubmit}>
<label>Event Title</label>
<input
placeholder="title"
name="title"
onChange={this.onInputChange}
value={event.title}
/>
<label>Venue</label>
<input
name="venue"
placeholder="Event Venue"
onChange={this.onInputChange}
value={event.venue}
/>
<button type="submit">
Submit
</button>
<button type="button" onClick={this.props.onCancel}>
Cancel
</button>
</form>
</div>
)
}
}
export default EventFort
newEvent绝对是对状态的引用,因此它直接改变状态。下面的代码证明:
onInputChange = event => {
const newEvent = this.state.event
newEvent[evt.target.name] = event.target.value
console.log(this.state.event) // => this.state.event {title: "a", venue: ""}
}
好了,现在证明状态是由控制台日志直接改变的,我的第一直觉是使用扩展运算符制作一个状态副本。
onInputChange = event => {
const newEvent = [...this.state.event]
newEvent[event.target.name] = event.target.value
this.setState({
event: newEvent
})
}
然而它可以正常工作,只要我输入输入,我就会在控制台上收到以下错误:
Warning: A component is changing a controlled input of type
undefined to be uncontrolled.
Input elements should not switch from controlled to uncontrolled (or
vice versa).
Decide between using a controlled or uncontrolled input element
for the lifetime of the component. More info:
in input (at EventForm.jsx:43)
in form (at EventForm.jsx:34)
in div (at EventForm.jsx:28)
in EventForm (at EventDashboard.jsx:90)
in div (created by GridColumn)
in GridColumn (at EventDashboard.jsx:87)
in div (created by Grid)
in Grid (at EventDashboard.jsx:83)
in EventDashboard (at App.jsx:14)
in div (created by Container)
in Container (at App.jsx:13)
in div (at App.jsx:11)
in App
in AppContainer (at index.js:16)
所以我确信有比原始代码更好的解决方案,即使它正在工作,它似乎也不是最好的方法。
答案 0 :(得分:1)
当您创建新对象时,不要直接在状态的对象上工作,只需将状态展开并覆盖值。
您可以使用computed keys将其设为通用:
onInputChange = ({target}) => {
const { event } = this.state;
const newEvent = {
...event,
[target.name]: target.value
}
this.setState({
event: newEvent
})
}
运行示例:
class EventForm extends React.Component {
state = {
event: {
title: "",
venue: ""
}
};
onFormSubmit = event => {
event.preventDefault();
//..
};
onInputChange = ({ target }) => {
const { event } = this.state;
const newEvent = {
...event,
[target.name]: target.value
};
this.setState({
event: newEvent
});
};
render() {
const { event } = this.state;
return (
<div>
<div>
Title: {this.state.event.title}
<br />
Venue: {this.state.event.venue}
</div>
<form onSubmit={this.onFormSubmit}>
<label>Event Title</label>
<input
placeholder="title"
name="title"
onChange={this.onInputChange}
value={event.title}
/>
<label>Venue</label>
<input
name="venue"
placeholder="Event Venue"
onChange={this.onInputChange}
value={event.venue}
/>
<button type="submit">Submit</button>
<button type="button" onClick={this.props.onCancel}>
Cancel
</button>
</form>
</div>
);
}
}
ReactDOM.render(<EventForm />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>