React 点击删除列表中对应项(React 获取DOM中自定义属性)

简介: 点击删除按钮,删除列表中对应项本来是React比较基础的应用,可是应用情况变得复杂了以后,我还真想了一会儿才搞定。简化一下应用场景:点击新增按钮,增加一条输入框,点击输入框旁边的按钮,删除该输入框(不能删错了啊)。

点击删除按钮,删除列表中对应项本来是React比较基础的应用,可是应用情况变得复杂了以后,我还真想了一会儿才搞定。

简化一下应用场景:点击新增按钮,增加一条输入框,点击输入框旁边的按钮,删除该输入框(不能删错了啊)。

  1. 先说第一种方法

问题刚上手,首先规划级别:一个输入框和对应删除按钮为一个子组件,整体为父组件即可方便处理。

注意的点:生成的一坨输入框是一个数组,为了准确删掉对应项,生成时要编号。点击删除按钮要反馈对应编号,然后进行删除。

现在的逻辑是:整个待展示列表(由子组件组成的数组)是个state,添加按钮会增加一个元素到这个state里面,添加的方法如下:

add(){

        var lists=this.state.lists;

        lists.push(<List key={this.state.lists.length} index={this.state.lists.length} delete={this.delete}/>);

        this.setState({lists:lists})

    }

注意一点,这个index属性是固定的,子组建生成后就固定了,这就为你未来挖了一个坑。

删除按钮当然就是从这个state列表里删除对应元素了,问题一来了,我怎么知道是第几个元素?一拍脑袋这还不简单,event.target 获取点击的标签,在标签上写个index属性告诉delete方法是第几个元素不就得了?试了发现不行,看看文档,event.target确实获取dom元素没毛病,但是index这个属性原生dom根本不承认啊,怎么办?data-index就行了,前面加 data- 就是dom承认的自定义属性了。

写完了又想起了两个方法,一个是在删除按钮绑定删除事件的时候,.bind(this,index),index是你准备删掉的是第几个或者表示出来你要删哪个就行。另一个是搞个闭包,就能把index参数传进去了(事件绑定一个立即执行的方程传入参数,该方程返回目标方程)。

第一个问题解决,删除的方法如下:

delete(e){

        var index=e.target.getAttribute("data-index");

        var lists=this.state.lists;

        lists.splice(index,1);

        this.setState({lists:lists})

    }

data-index告诉你要删除第几个元素,然后把它从state里踢出去就行了。这回掉进了一个真正意义上的坑:有时候删的不是对应的元素!乱套了!

好吧,我沉思了5分钟,想到了为什么:生成列表的时候index已经固定,但删除列表的时候我们只告诉他删除的是第index项!问题严重了,举个例子,有两项,index 0和1 你点0,好吧第0项删掉了,你再点1,疑?没反应了,因为你打算删除第1项,而列表中目前只有第0项(就是原来的第一项,原来的第0项删除后他就成了第0项)!这会导致各种乱套,考虑到生成列表的index是列表长度表示的就更乱了。

解决方式:delete方法里修改一行:

lists.splice(index,1,"");

好了,删除的元素我用空字符串代替,这样顺序和删除的项,还有以后添加的项的index都不会乱了,给自己点赞。到这里第一种方法实现了目标。

Code pen 地址:http://codepen.io/huanqingli/pen/dNyQez

完整代码:

class List extends React.Component {

    render() {

        return (<div><input type="text" defaultValue={this.props.index}/>

            <span onClick={this.props.delete} data-index={this.props.index}>X</span></div>)

    }

}

class Lists extends React.Component {

    constructor(props) {

        super(props);

        this.add=this.add.bind(this);

        this.delete=this.delete.bind(this);

        this.state={

            lists:[]

        }

    }

 

    add(){

        var lists=this.state.lists;

        lists.push(<List key={this.state.lists.length} index={this.state.lists.length} delete={this.delete}/>);

        this.setState({lists:lists})

    }

 

    delete(e){

        var index=e.target.getAttribute("data-index");

        var lists=this.state.lists;

        console.log(index)

        lists.splice(index,1);

        this.setState({lists:lists})

       

    }

 

    render() {

        return (<div>

            <span onClick={this.add}>添加</span>

            {this.state.lists}

            </div>)

    }

}

ReactDOM.render(

    <Lists/>,

    document.getElementById('lists')

);

这种方法有利有弊,所以我找到了第二种方法,具体情况择优使用。

  1. 第二种方法。总体来讲推荐这种方法。

在state里保存要展示的数据,在render里动态生成子组件组,然后添加删除都是操作保存数据的state,render里的子组件会自动刷新。这种方式应该是更贴近React思路的,用数据展现界面。如果你要展现一组数据,这种方法很自然,但如果展现的是一个动态的表单,稍微麻烦一点,但也可以做,而且我依然推荐用这种方式。

这种方法做个todolist就很简单,这里依然做上文的例子,稍微麻烦一点,也会理解的更深入一点。

整体结构和第一种方法一样,只不过这次state里面不是子组件,先用空字符串组成的数组代替,仅仅是为了render的时候知道有几个子组件而已。添加的时候也要push空字符串,等输入框输入数据后,更新state中的内容,做到数据和界面同步。

render子组件的部分:

{this.state.lists.map(function (item,index) {

                return <List key={index} index={index} delete={this.delete}/>

            }.bind(this))}

添加的方法变成:

add(){

        var lists=this.state.lists;

        lists.push("");

        this.setState({lists:lists})

    }

这就能跑了,这有个小坑,稍有不慎你发现你怎么删都是删列表的最后一项,其实数据操作没问题,关键是这个存在感比较低的key,必须特定项有给定的key你用动态的index他就懵了,不知道删哪个了,他就吧最后一个删了。废话不多说(该程序因为key键取值的问题有一个小问题)

Code pen 地址:http://codepen.io/huanqingli/pen/xgxNYN

整体代码:

  1 class List extends React.Component {
  2 
  3     constructor(props){
  4 
  5         super(props);
  6 
  7         this.upData=this.upData.bind(this);
  8 
  9     }
 10 
 11    
 12 
 13     upData(e){
 14 
 15         this.props.upData(this.props.index,e.target.value)
 16 
 17     }
 18 
 19  
 20 
 21     render() {
 22 
 23         return (<div><input type="text" onBlur={this.upData} defaultValue={this.props.item?this.props.item:""}/>
 24 
 25             <span onClick={this.props.delete} data-index={this.props.index}>X</span></div>)
 26 
 27     }
 28 
 29 }
 30 
 31 class Lists extends React.Component {
 32 
 33     constructor(props) {
 34 
 35         super(props);
 36 
 37         this.add=this.add.bind(this);
 38 
 39         this.delete=this.delete.bind(this);
 40 
 41         this.upData=this.upData.bind(this);
 42 
 43         this.state={
 44 
 45             lists:[]
 46 
 47         }
 48 
 49     }
 50 
 51  
 52 
 53     add(){
 54 
 55         var lists=this.state.lists;
 56 
 57         lists.push("");
 58 
 59         this.setState({lists:lists})
 60 
 61     }
 62 
 63  
 64 
 65     delete(e){
 66 
 67         var index=e.target.getAttribute("data-index");
 68 
 69         var lists=this.state.lists;
 70 
 71         lists.splice(index,1);
 72 
 73         this.setState({lists:lists})
 74 
 75     }
 76 
 77  
 78 
 79     upData(i,x){
 80 
 81         var lists=this.state.lists;
 82 
 83         lists[i]=x;
 84 
 85         console.log(lists);
 86 
 87         this.setState({lists:lists});
 88 
 89     }
 90 
 91  
 92 
 93     render() {
 94 
 95         return (<div>
 96 
 97             <span onClick={this.add}>添加</span>
 98 
 99             {this.state.lists.map(function (item,index) {
100 
101                 return <List key={item?item:index} index={index} delete={this.delete} upData={this.upData}  item={item}/>
102 
103             }.bind(this))}
104 
105             </div>)
106 
107     }
108 
109 }
110 
111  
112 
113 ReactDOM.render(
114 
115   <Lists />, document.getElementById('lists')
116 
117 )

 

这种方法经常也会有点小坑,也比较好解决。

总结:两种方法各有利弊,推荐第二种,符合REACT设计思路,但第一种有时候解决问题很方便。

相关文章
|
人工智能 自然语言处理 前端开发
SpringBoot + 通义千问 + 自定义React组件:支持EventStream数据解析的技术实践
【10月更文挑战第7天】在现代Web开发中,集成多种技术栈以实现复杂的功能需求已成为常态。本文将详细介绍如何使用SpringBoot作为后端框架,结合阿里巴巴的通义千问(一个强大的自然语言处理服务),并通过自定义React组件来支持服务器发送事件(SSE, Server-Sent Events)的EventStream数据解析。这一组合不仅能够实现高效的实时通信,还能利用AI技术提升用户体验。
1342 2
|
移动开发 前端开发 JavaScript
React音频播放列表组件:常见问题、易错点与解决方案
本文介绍了在React中实现音频播放列表时常见的挑战及解决方案。通过基础实现、常见问题分析和最佳实践,帮助开发者避免状态管理、生命周期控制和事件处理中的陷阱。关键点包括使用`useRef`操作音频元素、`useState`同步播放状态、全局状态管理防止多音频同时播放、以及通过`useEffect`清理资源。还提供了代码示例和跨浏览器兼容性处理方法,确保高效实现功能并减少调试时间。
416 30
|
编解码 前端开发 开发者
React 图片组件样式自定义:常见问题与解决方案
在 React 开发中,图片组件的样式自定义常因细节问题导致布局错乱、性能损耗或交互异常。本文系统梳理常见问题及解决方案,涵盖基础样式应用、响应式设计、加载状态与性能优化等,结合代码案例帮助开发者高效实现图片组件的样式控制。重点解决图片尺寸不匹配、边框阴影不一致、移动端显示模糊、加载失败处理及懒加载等问题,并总结易错点和最佳实践,助力开发者提升开发效率和用户体验。
509 22
|
前端开发 数据可视化 测试技术
React音频播放列表组件开发实战:常见问题与避坑指南
本文介绍了构建React音频播放列表组件的核心架构与常见问题解决方案。通过管理播放状态、列表索引和音频进度,结合异步控制、状态清理、节流优化等技术,确保流畅的用户体验。针对移动端兼容性、内存泄漏、列表渲染性能等问题提供了具体修复方案,并分享了自定义Hook封装、可视化音频波形等进阶实践。最后,总结了性能优化法则和测试关键点,帮助开发者打造生产级可靠的音频播放组件。
364 18
|
Web App开发 移动开发 前端开发
React 视频播放器样式自定义实战指南
本文详细介绍了如何在React项目中实现视频播放器的样式自定义,涵盖HTML5 `&lt;video&gt;`标签的基础知识、CSS样式定制技巧及常见问题解决方案。针对全屏模式样式失效、移动端触摸事件冲突和进度条样式定制等问题提供了具体代码示例。同时,探讨了视频预加载策略和内存优化方法,并推荐了几款调试工具,帮助开发者提升用户体验和应用性能。
440 6
|
Web App开发 移动开发 前端开发
React音频播放器样式自定义全解析:从入门到避坑指南
在React中使用HTML5原生&lt;audio&gt;标签时,开发者常面临视觉一致性缺失、样式定制局限和交互体验割裂等问题。通过隐藏原生控件并构建自定义UI层,可以实现完全可控的播放器视觉风格,避免状态不同步等典型问题。结合事件监听、进度条拖拽、浏览器兼容性处理及性能优化技巧,可构建高性能、可维护的音频组件,满足跨平台需求。建议优先使用成熟音频库(如react-player),仅在深度定制需求时采用原生方案。
563 12
|
JavaScript
DOM 节点列表长度(Node List Length)
DOM 节点列表长度(Node List Length)
|
移动开发 JavaScript 前端开发
原生js如何获取dom元素的自定义属性
原生js如何获取dom元素的自定义属性
670 4
|
JavaScript
DOM 节点列表长度(Node List Length)
DOM 节点列表长度(Node List Length)
|
JavaScript 前端开发 算法
React 虚拟 DOM 深度解析
【10月更文挑战第5天】本文深入解析了 React 虚拟 DOM 的工作原理,包括其基础概念、优点与缺点,以及 Diff 算法的关键点。同时,分享了常见问题及解决方法,并介绍了作者在代码/项目上的成就和经验,如大型电商平台的前端重构和开源贡献。
292 3