React:更新状态对象数组的正确方法
React: Proper way to update an array of objects in state
我是一名专业的 Web 开发人员,自学 React。我创建这个 table 作为一个更大的表格的一部分。
table 在表单组件内部调用
<ProductList
products={this.state.products}
onChange={products => this.sendUpdate('products', products)}
/>
this.sendUpdate:
sendUpdate(field, value) {
this.setState({[field]: value});
socket.emit('updateItem', this.state.id, {[field]: value});
}
这部分对我所有的表单更新都非常有用。但现在我想弄清楚如何处理 table 内的更新。每个产品都是这样调用的 table 的一行:
<tbody>
{this.props.products.map((product, i) =>
<Product key={i} data={product} products={this}/>
)}
</tbody>
当我输入其中一个输入时更新状态的正确方法是什么?
<FormControl
value={this.props.data.species}
onClick={e => this.updateProduct('species', e.target.value)}
/>
ProductList 的完整代码
import React from "react";
import {Button, Table, FormControl} from "react-bootstrap";
class Product extends React.Component {
updateField(...props){
this.props.products.updateProduct(this.data, ...props)
}
render() {
return (
<tr>
<td>
<FormControl
value={this.props.data.species}
onClick={e => this.updateProduct('species', e.target.value)}
/>
</td>
<td><FormControl/></td>
<td><FormControl/></td>
<td><FormControl/></td>
<td><FormControl/></td>
<td><FormControl/></td>
<td><FormControl type="number"/></td>
<td><Button bsStyle="danger" onClick={() => this.props.products.deleteProduct(this.props.data)}>X</Button></td>
</tr>
);
}
}
export default class ProductList extends React.Component {
constructor(...props) {
super(...props);
}
addProduct() {
let products = this.props.products.concat([{timestamp: Date.now()}]);
this.props.onChange(products);
}
updateProduct(product, field, newValue) {
this.props.products;
// ???
}
deleteProduct(product) {
let products = this.props.products.filter(p => {
return p !== product
});
this.props.onChange(products);
}
render() {
return (
<Table responsive>
<thead>
<tr>
<th>Species</th>
<th>Dried</th>
<th>Cut</th>
<th>Dimensions Green</th>
<th>Dimensions Dry</th>
<th>Color</th>
<th>Quantity</th>
<th className="text-right">
<Button bsStyle="success" bsSize="xsmall" onClick={() => this.addProduct()}>Add</Button>
</th>
</tr>
</thead>
<tbody>
{this.props.products.map(product => <Product key={product.timestamp} data={product} products={this}/>)}
</tbody>
</Table>
);
}
}
这是我根据接受的答案得出的结论:
import React from "react";
import {Button, Table, FormControl} from "react-bootstrap";
export default class ProductList extends React.Component {
constructor(...props) {
super(...props);
}
addProduct() {
let products = this.props.products.concat([{}]);
this.props.onChange(products);
}
updateProduct(product, field, newValue) {
const products = this.props.products.map(p => {
return p === product ? {...p, [field]: newValue} : p;
});
this.props.onChange(products);
}
deleteProduct(product) {
let products = this.props.products.filter(p => {
return p !== product
});
this.props.onChange(products);
}
render() {
return (
<Table responsive striped>
<thead>
<tr>
<th>Species</th>
<th>Dried</th>
<th>Cut</th>
<th>Dimensions Green</th>
<th>Dimensions Dry</th>
<th>Color</th>
<th>Quantity</th>
<th className="text-right">
<Button bsStyle="success" bsSize="xsmall" onClick={() => this.addProduct()}>Add</Button>
</th>
</tr>
</thead>
<tbody>
{this.props.products.map((product, i) => this.renderRow(i, product, this))}
</tbody>
</Table>
);
}
renderRow(i, product) {
return (
<tr key={i}>
<td>
<FormControl
value={product.species || ''}
onChange={e => this.updateProduct(product, 'species', e.target.value)}
/>
</td>
<td>
<FormControl
value={product.dried || ''}
onChange={e => this.updateProduct(product, 'dried', e.target.value)}
/>
</td>
<td>
<FormControl
value={product.cut || ''}
onChange={e => this.updateProduct(product, 'cut', e.target.value)}
/>
</td>
<td>
<FormControl
value={product.dimensionsGreen || ''}
onChange={e => this.updateProduct(product, 'dimensionsGreen', e.target.value)}
/>
</td>
<td>
<FormControl
value={product.dimensionsDry || ''}
onChange={e => this.updateProduct(product, 'dimensionsDry', e.target.value)}
/>
</td>
<td>
<FormControl
value={product.color || ''}
onChange={e => this.updateProduct(product, 'color', e.target.value)}
/>
</td>
<td>
<FormControl
type="number"
value={product.quantity || 0}
onChange={e => this.updateProduct(product, 'quantity', e.target.value)}
/>
</td>
<td><Button bsStyle="danger" onClick={() => this.deleteProduct(product)}>X</Button></td>
</tr>
);
}
}
在您的 ProductsList
的 render()
中,将数组映射更改为如下内容:
{this.props.products.map((product, index) => <Product key={product.timestamp} data={product} index={index} products={this}/>)}
然后在您的 Product
中将 updateField()
更改为:
updateField(...props){
this.props.products.updateProduct(this.props.index, ...props)
}
最后,将 ProductsList
的 updateProduct()
更改为:
updateProduct(index, field, newValue) {
const products = this.props.products.map((product, productIndex)) => {
if (index === productIndex) {
return {
...product,
[field]: newValue
};
}
return product;
})
this.props.onChange(products);
}
此外,Product
渲染中有一个轻微的拼写错误。 FormControl
的 onClick
应显示为 onClick={e => this.updateField('species', e.target.value)}
。
我是一名专业的 Web 开发人员,自学 React。我创建这个 table 作为一个更大的表格的一部分。
<ProductList
products={this.state.products}
onChange={products => this.sendUpdate('products', products)}
/>
this.sendUpdate:
sendUpdate(field, value) {
this.setState({[field]: value});
socket.emit('updateItem', this.state.id, {[field]: value});
}
这部分对我所有的表单更新都非常有用。但现在我想弄清楚如何处理 table 内的更新。每个产品都是这样调用的 table 的一行:
<tbody>
{this.props.products.map((product, i) =>
<Product key={i} data={product} products={this}/>
)}
</tbody>
当我输入其中一个输入时更新状态的正确方法是什么?
<FormControl
value={this.props.data.species}
onClick={e => this.updateProduct('species', e.target.value)}
/>
ProductList 的完整代码
import React from "react";
import {Button, Table, FormControl} from "react-bootstrap";
class Product extends React.Component {
updateField(...props){
this.props.products.updateProduct(this.data, ...props)
}
render() {
return (
<tr>
<td>
<FormControl
value={this.props.data.species}
onClick={e => this.updateProduct('species', e.target.value)}
/>
</td>
<td><FormControl/></td>
<td><FormControl/></td>
<td><FormControl/></td>
<td><FormControl/></td>
<td><FormControl/></td>
<td><FormControl type="number"/></td>
<td><Button bsStyle="danger" onClick={() => this.props.products.deleteProduct(this.props.data)}>X</Button></td>
</tr>
);
}
}
export default class ProductList extends React.Component {
constructor(...props) {
super(...props);
}
addProduct() {
let products = this.props.products.concat([{timestamp: Date.now()}]);
this.props.onChange(products);
}
updateProduct(product, field, newValue) {
this.props.products;
// ???
}
deleteProduct(product) {
let products = this.props.products.filter(p => {
return p !== product
});
this.props.onChange(products);
}
render() {
return (
<Table responsive>
<thead>
<tr>
<th>Species</th>
<th>Dried</th>
<th>Cut</th>
<th>Dimensions Green</th>
<th>Dimensions Dry</th>
<th>Color</th>
<th>Quantity</th>
<th className="text-right">
<Button bsStyle="success" bsSize="xsmall" onClick={() => this.addProduct()}>Add</Button>
</th>
</tr>
</thead>
<tbody>
{this.props.products.map(product => <Product key={product.timestamp} data={product} products={this}/>)}
</tbody>
</Table>
);
}
}
这是我根据接受的答案得出的结论:
import React from "react";
import {Button, Table, FormControl} from "react-bootstrap";
export default class ProductList extends React.Component {
constructor(...props) {
super(...props);
}
addProduct() {
let products = this.props.products.concat([{}]);
this.props.onChange(products);
}
updateProduct(product, field, newValue) {
const products = this.props.products.map(p => {
return p === product ? {...p, [field]: newValue} : p;
});
this.props.onChange(products);
}
deleteProduct(product) {
let products = this.props.products.filter(p => {
return p !== product
});
this.props.onChange(products);
}
render() {
return (
<Table responsive striped>
<thead>
<tr>
<th>Species</th>
<th>Dried</th>
<th>Cut</th>
<th>Dimensions Green</th>
<th>Dimensions Dry</th>
<th>Color</th>
<th>Quantity</th>
<th className="text-right">
<Button bsStyle="success" bsSize="xsmall" onClick={() => this.addProduct()}>Add</Button>
</th>
</tr>
</thead>
<tbody>
{this.props.products.map((product, i) => this.renderRow(i, product, this))}
</tbody>
</Table>
);
}
renderRow(i, product) {
return (
<tr key={i}>
<td>
<FormControl
value={product.species || ''}
onChange={e => this.updateProduct(product, 'species', e.target.value)}
/>
</td>
<td>
<FormControl
value={product.dried || ''}
onChange={e => this.updateProduct(product, 'dried', e.target.value)}
/>
</td>
<td>
<FormControl
value={product.cut || ''}
onChange={e => this.updateProduct(product, 'cut', e.target.value)}
/>
</td>
<td>
<FormControl
value={product.dimensionsGreen || ''}
onChange={e => this.updateProduct(product, 'dimensionsGreen', e.target.value)}
/>
</td>
<td>
<FormControl
value={product.dimensionsDry || ''}
onChange={e => this.updateProduct(product, 'dimensionsDry', e.target.value)}
/>
</td>
<td>
<FormControl
value={product.color || ''}
onChange={e => this.updateProduct(product, 'color', e.target.value)}
/>
</td>
<td>
<FormControl
type="number"
value={product.quantity || 0}
onChange={e => this.updateProduct(product, 'quantity', e.target.value)}
/>
</td>
<td><Button bsStyle="danger" onClick={() => this.deleteProduct(product)}>X</Button></td>
</tr>
);
}
}
在您的 ProductsList
的 render()
中,将数组映射更改为如下内容:
{this.props.products.map((product, index) => <Product key={product.timestamp} data={product} index={index} products={this}/>)}
然后在您的 Product
中将 updateField()
更改为:
updateField(...props){
this.props.products.updateProduct(this.props.index, ...props)
}
最后,将 ProductsList
的 updateProduct()
更改为:
updateProduct(index, field, newValue) {
const products = this.props.products.map((product, productIndex)) => {
if (index === productIndex) {
return {
...product,
[field]: newValue
};
}
return product;
})
this.props.onChange(products);
}
此外,Product
渲染中有一个轻微的拼写错误。 FormControl
的 onClick
应显示为 onClick={e => this.updateField('species', e.target.value)}
。