点击删除按钮,删除列表中对应项本来是React比较基础的应用,可是应用情况变得复杂了以后,我还真想了一会儿才搞定。@H_403_4@@H_403_4@@H_403_4@
简化一下应用场景:点击新增按钮,增加一条输入框,点击输入框旁边的按钮,删除该输入框(不能删错了啊)。
问题刚上手,首先规划级别:一个输入框和对应删除按钮为一个子组件,整体为父组件即可方便处理。
注意的点:生成的一坨输入框是一个数组,为了准确删掉对应项,生成时要编号。点击删除按钮要反馈对应编号,然后进行删除。
现在的逻辑是:整个待展示列表(由子组件组成的数组)是个state,添加按钮会增加一个元素到这个state里面,添加的方法如下:@H_403_4@@H_403_4@@H_403_4@
add(){
@H_403_4@var@H_403_4@ lists=this@H_403_4@.state.lists;
lists.push(@H_403_4@<List key={this@H_403_4@.state.lists.length} index={this@H_403_4@.state.lists.length} delete@H_403_4@={this@H_403_4@.delete@H_403_4@}/>);
this@H_403_4@.setState({lists:lists})
}@H_403_4@
注意一点,这个index属性是固定的,子组建生成后就固定了,这就为你未来挖了一个坑。
删除按钮当然就是从这个state列表里删除对应元素了,问题一来了,我怎么知道是第几个元素?一拍脑袋这还不简单,event.target 获取点击的标签,在标签上写个index属性告诉delete方法是第几个元素不就得了?试了发现不行,看看文档,event.target确实获取dom元素没毛病,但是index这个属性原生dom根本不承认啊,怎么办?data-index就行了,前面加 data- 就是dom承认的自定义属性了。@H_403_4@@H_403_4@@H_403_4@@H_403_4@
写完了又想起了两个方法,一个是在删除按钮绑定删除事件的时候,.bind(this,index),index是你准备删掉的是第几个或者表示出来你要删哪个就行。另一个是搞个闭包,就能把index参数传进去了(事件绑定一个立即执行的方程传入参数,该方程返回目标方程)。@H_403_4@@H_403_4@
delete@H_403_4@(e){
@H_403_4@var@H_403_4@ index=e.target.getAttribute("data-index");
@H_403_4@var@H_403_4@ lists=this@H_403_4@.state.lists;
lists.splice(index,@H_403_4@1);
@H_403_4@this@H_403_4@.setState({lists:lists})
}@H_403_4@
data-index告诉你要删除第几个元素,然后把它从state里踢出去就行了。这回掉进了一个真正意义上的坑:有时候删的不是对应的元素!乱套了!
好吧,我沉思了5分钟,想到了为什么:生成列表的时候index已经固定,但删除列表的时候我们只告诉他删除的是第index项!问题严重了,举个例子,有两项,index 0和1 你点0,好吧第0项删掉了,你再点1,疑?没反应了,因为你打算删除第1项,而列表中目前只有第0项(就是原来的第一项,原来的第0项删除后他就成了第0项)!这会导致各种乱套,考虑到生成列表的index是列表长度表示的就更乱了。@H_403_4@@H_403_4@@H_403_4@@H_403_4@@H_403_4@
lists.splice(index,1,"");
好了,删除的元素我用空字符串代替,这样顺序和删除的项,还有以后添加的项的index都不会乱了,给自己点赞。到这里第一种方法实现了目标。@H_403_4@@H_403_4@@H_403_4@@H_403_4@
Code pen 地址:http://codepen.io/huanqingli/pen/dNyQez@H_403_4@
完整代码:
class List extends React.Component {
render() {
@H_403_4@return@H_403_4@ (<div><input type="text" defaultValue={this@H_403_4@.props.index}/>
<span onClick={this@H_403_4@.props.delete@H_403_4@} data-index={this@H_403_4@.props.index}>X</span></div>)
}
}
class Lists extends React.Component {
constructor(props) {
super(props);
@H_403_4@this@H_403_4@.add=this@H_403_4@.add.bind(this@H_403_4@);
@H_403_4@this@H_403_4@.delete@H_403_4@=this@H_403_4@.delete@H_403_4@.bind(this@H_403_4@);
@H_403_4@this@H_403_4@.state={
lists:[]
}
}
add(){
@H_403_4@var@H_403_4@ lists=this@H_403_4@.state.lists;
lists.push(@H_403_4@<List key={this@H_403_4@.state.lists.length} index={this@H_403_4@.state.lists.length} delete@H_403_4@={this@H_403_4@.delete@H_403_4@}/>);
this@H_403_4@.setState({lists:lists})
}
@H_403_4@delete@H_403_4@(e){
@H_403_4@var@H_403_4@ index=e.target.getAttribute("data-index");
@H_403_4@var@H_403_4@ lists=this@H_403_4@.state.lists;
console.log(index)
lists.splice(index,@H_403_4@1);
@H_403_4@this@H_403_4@.setState({lists:lists})
}
render() {
@H_403_4@return@H_403_4@ (<div>
<span onClick={this@H_403_4@.add}>添加</span>
{@H_403_4@this@H_403_4@.state.lists}
@H_403_4@</div>)
}
}
ReactDOM.render(
@H_403_4@<Lists/>,
document.getElementById(@H_403_4@'lists')
);@H_403_4@
这种方法有利有弊,所以我找到了第二种方法,具体情况择优使用。
在state里保存要展示的数据,在render里动态生成子组件组,然后添加删除都是操作保存数据的state,render里的子组件会自动刷新。这种方式应该是更贴近React思路的,用数据展现界面。如果你要展现一组数据,这种方法很自然,但如果展现的是一个动态的表单,稍微麻烦一点,但也可以做,而且我依然推荐用这种方式。
这种方法做个todolist就很简单,这里依然做上文的例子,稍微麻烦一点,也会理解的更深入一点。
整体结构和第一种方法一样,只不过这次state里面不是子组件,先用空字符串组成的数组代替,仅仅是为了render的时候知道有几个子组件而已。添加的时候也要push空字符串,等输入框输入数据后,更新state中的内容,做到数据和界面同步。
render子组件的部分:
{this@H_403_4@.state.lists.map(function@H_403_4@ (item,index) {
@H_403_4@return@H_403_4@ <List key={index} index={index} delete@H_403_4@={this@H_403_4@.delete@H_403_4@}/>
}.bind(@H_403_4@this@H_403_4@))}
add(){
@H_403_4@var@H_403_4@ lists=this@H_403_4@.state.lists;
lists.push(@H_403_4@"");
@H_403_4@this@H_403_4@.setState({lists:lists})
}@H_403_4@
这就能跑了,这有个小坑,稍有不慎你发现你怎么删都是删列表的最后一项,其实数据操作没问题,关键是这个存在感比较低的key,必须特定项有给定的key你用动态的index他就懵了,不知道删哪个了,他就吧最后一个删了。废话不多说(该程序因为key键取值的问题有一个小问题):@H_403_4@@H_403_4@@H_403_4@@H_403_4@@H_403_4@
Code pen 地址:http://codepen.io/huanqingli/pen/xgxNYN@H_403_4@@H_403_4@
整体代码:
1@H_403_4@ class List extends React.Component {
@H_403_4@ 2@H_403_4@
3@H_403_4@ constructor(props){
@H_403_4@ 4@H_403_4@
5@H_403_4@ super(props);
@H_403_4@ 6@H_403_4@
7@H_403_4@ this@H_403_4@.upData=this@H_403_4@.upData.bind(this@H_403_4@);
@H_403_4@ 8@H_403_4@
9@H_403_4@ }
@H_403_4@ 10@H_403_4@
11@H_403_4@
12@H_403_4@
13@H_403_4@ upData(e){
@H_403_4@ 14@H_403_4@
15@H_403_4@ this@H_403_4@.props.upData(this@H_403_4@.props.index,e.target.value)
@H_403_4@ 16@H_403_4@
17@H_403_4@ }
@H_403_4@ 18@H_403_4@
19@H_403_4@
20@H_403_4@
21@H_403_4@ render() {
@H_403_4@ 22@H_403_4@
23@H_403_4@ return@H_403_4@ (<div><input type="text" onBlur={this@H_403_4@.upData} defaultValue={this@H_403_4@.props.item?this@H_403_4@.props.item:""}/>
24@H_403_4@
25@H_403_4@ <span onClick={this@H_403_4@.props.delete@H_403_4@} data-index={this@H_403_4@.props.index}>X</span></div>)
@H_403_4@ 26@H_403_4@
27@H_403_4@ }
@H_403_4@ 28@H_403_4@
29@H_403_4@ }
@H_403_4@ 30@H_403_4@
31@H_403_4@ class Lists extends React.Component {
@H_403_4@ 32@H_403_4@
33@H_403_4@ constructor(props) {
@H_403_4@ 34@H_403_4@
35@H_403_4@ super(props);
@H_403_4@ 36@H_403_4@
37@H_403_4@ this@H_403_4@.add=this@H_403_4@.add.bind(this@H_403_4@);
@H_403_4@ 38@H_403_4@
39@H_403_4@ this@H_403_4@.delete@H_403_4@=this@H_403_4@.delete@H_403_4@.bind(this@H_403_4@);
@H_403_4@ 40@H_403_4@
41@H_403_4@ this@H_403_4@.upData=this@H_403_4@.upData.bind(this@H_403_4@);
@H_403_4@ 42@H_403_4@
43@H_403_4@ this@H_403_4@.state={
@H_403_4@ 44@H_403_4@
45@H_403_4@ lists:[]
@H_403_4@ 46@H_403_4@
47@H_403_4@ }
@H_403_4@ 48@H_403_4@
49@H_403_4@ }
@H_403_4@ 50@H_403_4@
51@H_403_4@
52@H_403_4@
53@H_403_4@ add(){
@H_403_4@ 54@H_403_4@
55@H_403_4@ var@H_403_4@ lists=this@H_403_4@.state.lists;
@H_403_4@ 56@H_403_4@
57@H_403_4@ lists.push("");
@H_403_4@ 58@H_403_4@
59@H_403_4@ this@H_403_4@.setState({lists:lists})
@H_403_4@ 60@H_403_4@
61@H_403_4@ }
@H_403_4@ 62@H_403_4@
63@H_403_4@
64@H_403_4@
65@H_403_4@ delete@H_403_4@(e){
@H_403_4@ 66@H_403_4@
67@H_403_4@ var@H_403_4@ index=e.target.getAttribute("data-index");
@H_403_4@ 68@H_403_4@
69@H_403_4@ var@H_403_4@ lists=this@H_403_4@.state.lists;
@H_403_4@ 70@H_403_4@
71@H_403_4@ lists.splice(index,1);
@H_403_4@ 72@H_403_4@
73@H_403_4@ this@H_403_4@.setState({lists:lists})
@H_403_4@ 74@H_403_4@
75@H_403_4@ }
@H_403_4@ 76@H_403_4@
77@H_403_4@
78@H_403_4@
79@H_403_4@ upData(i,x){
@H_403_4@ 80@H_403_4@
81@H_403_4@ var@H_403_4@ lists=this@H_403_4@.state.lists;
@H_403_4@ 82@H_403_4@
83@H_403_4@ lists[i]=x;
@H_403_4@ 84@H_403_4@
85@H_403_4@ console.log(lists);
@H_403_4@ 86@H_403_4@
87@H_403_4@ this@H_403_4@.setState({lists:lists});
@H_403_4@ 88@H_403_4@
89@H_403_4@ }
@H_403_4@ 90@H_403_4@
91@H_403_4@
92@H_403_4@
93@H_403_4@ render() {
@H_403_4@ 94@H_403_4@
95@H_403_4@ return@H_403_4@ (<div>
96@H_403_4@
97@H_403_4@ <span onClick={this@H_403_4@.add}>添加</span>
98@H_403_4@
99@H_403_4@ {this@H_403_4@.state.lists.map(function@H_403_4@ (item,index) {
@H_403_4@100@H_403_4@
101@H_403_4@ return@H_403_4@ <List key={item?item:index} index={index} delete@H_403_4@={this@H_403_4@.delete@H_403_4@} upData={this@H_403_4@.upData} item={item}/>
102@H_403_4@
103@H_403_4@ }.bind(this@H_403_4@))}
@H_403_4@104@H_403_4@
105@H_403_4@ </div>)
106@H_403_4@
107@H_403_4@ }
@H_403_4@108@H_403_4@
109@H_403_4@ }
@H_403_4@110@H_403_4@
111@H_403_4@
112@H_403_4@
113@H_403_4@ ReactDOM.render(
@H_403_4@114@H_403_4@
115@H_403_4@ <Lists />,document.getElementById('lists')
116@H_403_4@
117@H_403_4@ )