React 點擊刪除列表中對應項(React 獲取DOM中自定義屬性)


點擊刪除按鈕,刪除列表中對應項本來是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設計思路,但第一種有時候解決問題很方便。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM