为什么React列表切片总是删除最后一行

时间:2019-01-29 20:31:15

标签: asp.net-mvc reactjs

我在Angular中有类似的代码,效果很好。我试图将代码转换为React版本。效果很好,除非我尝试删除行。每次删除单击都会删除最后一行。我的期望是删除我单击“删除”按钮的行。我正在传递需要删除的索引项。我提供了以下示例,您也可以在这里尝试... https://jsfiddle.net/w6cy0bx1/11/

class MineCts extends React.Component{
constructor(props){
super(props);
this.state = {
items:[]
};
}

render(){
return(
<div>
  <div><button onClick={(event)=>this.add(event)} >Add</button></div>
  <div>
    This is sample:
    {this.state.items.map((record, index)=>{
    return(
    <table>
      <tr key={index}>
        <td> <input id="Name" value={record.Name} onChange={this.onDataChange}/> </td>
        <td> <input id="Counter" value={record.Counter} onChange={this.onDataChange} /> </td>
        <td><button onClick={(event)=>this.remove(event, record, index)} >Remove</button></td>
      </tr>
    </table>
    )
    })}

  </div>
</div>
)
}

onDataChange(e)
{
console.log(e.target.id)
this.setState({[e.target.id]:e.target.value})
}

add(e){
let rows = this.state.items;
rows.push({});
this.setState({items:rows})
}

remove(e, record, index){
console.log(record)
console.log(index)
let rows = this.state.items;
rows.splice(index, 1);
this.setState({items:rows})
}

}



ReactDOM.render(<MineCts />, mountNode)

3 个答案:

答案 0 :(得分:1)

仔细检查后,发现问题的原因是

      <tr key={record.index}>

实际上并没有设置,您没有给孩子使用的唯一密钥,因此React不知道这些孩子中的哪个已被修改,因此当您删除其中一个孩子时,它会有些混乱。 / p>

您需要为每个记录提供唯一的密钥。

我更新了您的代码,并使用new Date().getTime()作为获取唯一密钥的快速方法。

我在https://jsfiddle.net/xk4e1z2s/2/

处有一个工作示例

在添加每个记录的唯一键时,您会看到它们的值,并且您会注意到只有特定的行被删除了。

在删除一行后,输入字段为空的原因是,您需要保存记录的输入值。

答案 1 :(得分:1)

您可以在删除功能上直接执行setState。

我的意思是;

remove(index) {
  this.setState({
    items: this.state.items.filter((s, sindex) => index !== sindex),
  });
}

答案 2 :(得分:0)

感谢大家的反馈。这非常有帮助。我了解到,如果要使用React行,则需要具有唯一的键。使用过滤器代替“拼接”也很有帮助。我必须添加更改事件。将一个组件组合成多个组件后,我发现它非常有用。我更新了解决方案,以便在删除单行后不会清除输入字段。我还意识到,与Angular React不同,它只执行一种方式绑定,并且每个数据更改都必须通过状态进行更新。这是完整的解决方案:https://jsfiddle.net/wfcev8zb/30/。特别感谢user2340824的持续反馈。 这是最终代码:

class MineCts extends React.Component{
constructor(props){
super(props);
this.state = {
items:[]
};

this.add = this.add.bind(this);
this.onDataChange = this.onDataChange.bind(this);
this.remove = this.remove.bind(this);
}

render(){
return(
<div>
  <div><button onClick={this.add} >Add</button></div>
  <div>
    This is sample:

{this.state.items.map((record, index)=>{
    return(
    <DetailTable record ={record} callOnChangeFunc={this.onDataChange} callOnRemoveFunc={this.remove} />
  );
    })}

      </div>
</div>
)
}

onDataChange(e, record)
{
e.preventDefault();
let rows = [...this.state.items];
record[e.target.id]=e.target.value;
rows[record.Id] = record;
this.setState({items:rows})
}

add(e){
let rows = [...this.state.items];
rows.push({Id: new Date().getTime(), Name:null, Counter:null});
this.setState({items:rows})
}

remove(e, record){
e.preventDefault();
let rows = [...this.state.items.filter(x=>x.Id!==record.Id)];
this.setState({items:rows})
}

}

class DetailTable extends React.Component{
constructor(props){
super();
this.callOnChange = this.callOnChange.bind(this);
this.callOnRemove = this.callOnRemove.bind(this);
}
callOnChange(e){
this.props.callOnChangeFunc(e, this.props.record);
}
callOnRemove(e){
this.props.callOnRemoveFunc(e, this.props.record);
}

render(){
return (
<div>
  <table>  
      <tr key={this.props.record.Id} >
        {this.props.record.Id}
        <td> 
        <input id="Name" value={this.props.record.Name} onChange={this.callOnChange} /> </td>
        <td> 
        <input id="Counter" value={this.props.record.Counter}  onChange={this.callOnChange}  /> </td>
        <td><button onClick={this.callOnRemove} >Remove</button></td>
      </tr>
   </table>
</div>     
);
}
}

ReactDOM.render(<MineCts />, document.querySelector("#app"))