我有一个表部件。 该表的每一行也是组成部分。
class FormulaBuilder extends Component {
constructor(props) {
super(props);
this.state = {
rows: [{}]
}
}
handleAddRow = () => {
const item = {};
this.setState({
rows: [...this.state.rows, item]
});
};
handleRemoveSpecificRow = (idx) => {
const rows = [...this.state.rows]
rows.splice(idx, 1)
this.setState({ rows })
}
render() {
return (
{
this.state.rows.map((item, idx) => {
return (
<React.Fragment key={idx}>
<ConcoctionRow
removeSpecificRow={(idx) =>this.handleRemoveSpecificRow(idx)}
id={idx} />
</React.Fragment>);
})
});
}
}
在子组件中有一个按钮。单击后,将调用父组件中的事件:
class ConcoctionRow extends Component {
constructor(props) {
super(props);
}
handleRemoveSpecificRow = () => {
this.props.removeSpecificRow(this.props.id);
}
}
这些属性传递了数组的索引。但是只有最后一行总是被删除而不是特定的。
我的坏处在哪里?附言我是JS新手。
答案 0 :(得分:2)
几件事,您要避免使用.splice()更新组件中的数组。通常,这实际上最终会改变您的原始状态,而不是创建一个新状态。直接违反了React概念。
同样,让我们在控制台上尝试一些操作:
const arr = [1, 2, 3] <-- this is your state
const newArr = arr <-- you created a reference of your state. This does not actually create a new copy.
现在,如果您拼接newArr
newArr.splice(0, 1) <-- now newArr = [2, 3]
好吧,你还改变了原始状态。
arr <-- is now also [2, 3]
JavaScript中常见的误解是,当您创建一个等于现有变量的新变量时,您期望它实际上会创建一个新副本。
let cat = {id: 1, name: "bunny"}
let myCat = cat
实际上并非如此,您的新变量不是显式创建新副本,而是指向派生自其的原始对象的相同引用。如果我做了类似的事情:
myCat.age = 2 <-- Now myCat has a new property of age.
myCat <-- {id: 2, name: "bunny", age: 2}
但是,因为这两个变量指向相同的引用。您还可以对原始猫对象进行变异
cat <-- {id: 2, name: "bunny", age: 2}
使用array.filter()
来创建一个全新的数组。
以下是您的代码示例以及供参考的沙箱:https://codesandbox.io/s/heuristic-nobel-6ece5
import React from "react";
import ConcoctionRow from "./ConcoctionRow";
class FormulaBuilder extends React.Component {
constructor(props) {
super(props);
this.state = {
rows: [{}, {}, {}]
};
}
handleAddRow = () => {
const item = {};
this.setState({
rows: [...this.state.rows, item]
});
};
handleRemoveSpecificRow = idx => {
const { rows } = this.state;
const updatedRows = rows.filter((row, index) => {
return index !== idx;
});
this.setState({
rows: updatedRows
});
};
render() {
return (
<div>
{this.state.rows.map((item, idx) => {
return (
<React.Fragment key={idx}>
<ConcoctionRow
removeSpecificRow={this.handleRemoveSpecificRow}
id={idx}
/>
</React.Fragment>
);
})}
</div>
);
}
}
export default FormulaBuilder;
答案 1 :(得分:0)
我显示了在这种情况下要使用的模式。我建议对项目使用id
而不是数组索引。
filter
数组函数是不可变的(它会创建一个新数组,而不会使前一个数组发生变化),因此可以在设置状态下使用。 setState
的功能形式也不错。
const Row = ({ onClick, children, id }) => (
<li>{children} <button onClick={() => onClick(id)}>Delete</button></li>
)
class App extends React.Component {
state = {
list: [
{id: 1, label: 'foo' },
{id: 2, label: 'bar' }
]
}
handleDelete = id => {
this.setState(prevState => ({
list: prevState.list.filter(row => (
row.id !== id
))
}))
}
render(){
const { list } = this.state;
return (
<ul>
{list.map(({ id, label }) => (
<Row id={id} onClick={this.handleDelete}>{label}</Row>
))}
</ul>
)
}
}
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>