玩轉Reactjs第五篇-基礎知識(事件&條件&列表&表單)

一、前言

其實在前面介紹組件的時候,已經用到了這些知識,本章節我們重點分析下,並與Vue的用法比較。

二、事件

    Reactjs中事件處理方式與原生態DOM的非常類似,只不過使用JSX的語法,採用的是小駝峯的表達方式,比如onClick,onChange,我們以SearchHot.js的"下一批"功能爲例,當點擊按鈕,更新熱詞。

return (
        <div className="d"> 
            <div className="s">大家都在搜:<a href="#" onClick={this.changeHot} style={{marginLeft:100}}>下一批</a></div>
            <ul className="su">
                {/*引用變量表達式  */}
                 {hotList}
            </ul>

        </div> 
        )

在a標籤上增加onClick響應事件,調用的方法爲changeHot,下面我們對該方法定義

changeHot(){
        this.setState({hotItem:['電視','手機','電腦','平板']})
    }

在此方法中,我們調用setState更新hotItem屬性。如果直接運行,將會報this爲undefined的錯誤,我們還需要在組件初始化方法中,將該方法綁定到組件對象。

constructor(props){
        // 1、調用父類構造方法
        super(props);
        //2、初始化state對象
        this.state = {hotItem:['口罩','手套','酒精',]};
        //綁定this
        this.changeHot = this.changeHot.bind(this);
    }

除了這種bind方式,還可以將changeHot改造爲箭頭函數,在箭頭函數中,this指向的函數的本身,而不是函數的調用者,可以參考ES6系列教程第八篇--箭頭函數詳解

changeHot = () =>this.setState({hotItem:['電視','手機','電腦','平板']});

我們在a標籤上增加一個默認事件,href="search.json"

return (
        <div className="d"> 
            <div className="s">大家都在搜:<a href="search.json" onClick={this.changeHot} style={{marginLeft:100}}>下一批</a></div>
            <ul className="su">
                {/*引用變量表達式  */}
                 {hotList}
            </ul>

        </div> 
        )

此時再次點擊按鈕,發現頁面跳轉到search.json,這是因爲執行了默認事件。現在我們需要阻止默認事件或者冒泡。

changeHot(e){
        e.preventDefault();
        e.stopPropagation();
        this.setState({hotItem:['電視','手機','電腦','平板'] })
    }

再次點擊時,功能正常。

事件方法中會隱形傳入一個event參數,通過調用該參數的preventDefault(),stopPropagation()阻止默認事件以及冒泡。

傳參是事件的基本的功能,我們看下React如何實現傳參。

return (
        <div className="d"> 
            <div className="s">大家都在搜:<a href="search.json" onClick={this.changeHot.bind(this,1,"ok")} style={{marginLeft:100}}>下一批</a></div>
            <ul className="su">
                {/*引用變量表達式  */}
                 {hotList}
            </ul>

        </div> 
        )

在調用的時候綁定this對象,並依次傳遞相關的參數(注意:事件方法已經綁定了this,可以不需要再在初始化方法中重複綁定),在changeHot中接受並處理參數。

changeHot(id,name,e){
        e.preventDefault();
        e.stopPropagation();
        this.setState({hotItem:['電視','手機','電腦','平板'] });
        console.log("id:"+id);
        console.log("name:"+name)
    }

無論傳遞多少參數,隱形的event參數始終處於最後的位置。

在Vue中,事件的處理使用的是v-on內置的指令,並通過事件的修飾符簡化了諸如阻止默認事件,冒泡等寫法。而JSX語法中沒有指令的概念,其處理方式更接近原生的DOM。

三、條件

在前面章節中,我們介紹JSX僅支持三目以及&&符運算的嵌入,而不支持if...else條件語句。我們先看三目運算和&&。

在SearchBox.js中,我們用this.state.isSearch標誌符控制button按鈕的disabled屬性,當this.state.isSearchShow爲true,按鈕有效,false則無效。

        return(
            <div style={{width:300,margin:10}}>
                <input type="text" style={{width:150}} onChange={this.handleChange}></input>&nbsp;&nbsp;
               <button onClick={this.onSearchClick} disabled={this.state.isSearchShow?"":"disabled"}>搜索</button>
            </div>
        )

也可以改寫&&運算

return(
            <div style={{width:300,margin:10}}>
                <input type="text" style={{width:150}} onChange={this.handleChange}></input>&nbsp;&nbsp;
               <button onClick={this.onSearchClick} disabled={!this.state.isSearchShow&&"disabled"}>搜索</button>
            </div>
        )

在js中,true && expression 總是會返回 expression, 而 false && expression 總是會返回 false。

如果是多個條件組合,三目運算就無法滿足了,比如不同的狀態,顯示button的不同背景色,此時就需要用到if...else。

render(){
        //定義color變量,並根據狀態顯示不同的顏色
        let color;
        if(this.state.color<10){
            color="green"
        }else if(this.state.color<20){
            color="blue"
        }else if(this.state.color<30){
            color="red"
        }else{
            color="orange"
        }

        return(
            <div style={{width:300,margin:10}}>
                <input type="text" style={{width:150}} onChange={this.handleChange}></input>&nbsp;&nbsp;
               <button onClick={this.onSearchClick} disabled={!this.state.isSearchShow&&"disabled"} style={{backgroundColor:color}}>搜索</button>
            </div>
        )
    }

雖然JSX中無法直接使用if...else,但可以通過在代碼段中,定義color的變量並條件賦值,再在JSX中插入color變量表達式。

在Vue中使用v-if,v-else指令進行條件控制,並對控件的隱藏和顯示提供了v-show,使用更方便。

四、列表

Reactjs中對於列表使用map方法進行遍歷,並拼裝成JSX表達式。以SearchList.js的搜索結果列表展示爲例:

render(){
      //1、從props中獲取searchListVal,封裝列表
      const searchValList = this.state.searchListVal.map((value)=>
        (<li>
          {value}
        </li>)
      );

      return(
        <div className="d">
            <div className="s">搜索結果:</div>
            {/* 引入插槽代碼 */}
            {this.props.children}
            <ul className="u" ref={this.searchul}>
              {/* 引用對象表達式*/}
               {searchValList}
            </ul>
        </div>       
      )
    }

遍歷searchListVal數組,拼裝li表達式。此時我們打開控制檯,會發現有如下的warning

這個告警的意思,list應增加一個key屬性。

 const searchValList = this.state.searchListVal.map((value)=>
        (<li key={value.toString()}>
          {value}
        </li>)
      );

告警消失,那加上這個key有什麼作用呢?

這要從diff算法的原理說起。diff運算時,對於同一節點下的子元素,進行遍歷比較,如果不一致,則產出一個mutation。如下:

//老節點
<ul>
  <li>first</li>
  <li>second</li>
</ul>
//新節點
<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

該樣例中,ul的子元素li,新老節點中第一個和第二個是完全一樣的,所以無需變動,僅在尾部插入第三個li元素,就可以完成節點的更新,效率很高。如果是頭部插入一個li呢?

/老節點
<ul> 
  <li>first</li>
  <li>second</li>
</ul>
//新節點
<ul>
   <li>third</li>
  <li>first</li>
  <li>second</li>
</ul>

在diff運算過程中,比較發現第一個,第二個li元素不一樣(老節點的第一個爲first,新節點的第一個爲third),將會銷燬重建,效率會非常低。效率最高的的更新操作,僅在頭部插入一個third,其他節點保持不變。爲了解決這個問題,引入了key。

/老節點
<ul> 
  <li key="1">first</li>
  <li key="2">second</li>
</ul>
//新節點
<ul>
  <li key="3">third</li>
  <li key="1">first</li>
  <li key="2">second</li>
</ul>

diff運算過程中,不是直接遍歷比較,而是優先查找是否有相同的key,新老節點都有key爲1,2的li元素,且值沒有變化,這兩個節點將保持不變,最終僅在頭部插入key爲3的li元素,實現了高效率的更新。

所以,key需要有以下兩個特點:

  • key一定是在同級的兄弟節點間設置,因爲非同級節點不會進行diff運算。
  • key的值在兄弟節點間必須唯一,一般用id等,最好不要用列表索引(索引可能會變化)

在Vue用使用v-for進行列表渲染,其中也需要設置key,原理是一致的。

五、表單

表單標籤包括select,input,textarea,checkbox,radio等,他們是最能說明雙向綁定的過程,比如SearchBox.js中的搜索輸入框input

handleChange(e){
       this.setState({searchVal:e.target.value});
    }
....
return(
            <div style={{width:300,margin:10}}>
                <input type="text" style={{width:150}} onChange={this.handleChange} value={this.state.searchVal}></input>&nbsp;&nbsp;
               <button onClick={this.onSearchClick} >搜索</button>
            </div>
        )

input框的每次輸入,通過onChange捕捉變化事件,並響應handleChange方法,將input的當前值更新到state中的searchVal(這就是view->model過程);searchVal賦值給input的value,searchVal值的變化,又反饋到input的value(這就是model->view)。有同學會問,這這麼做不是多此一舉麼,其實不然,在實際使用過程中,我們會在handleChange中對輸入值e.target.value進行處理,比如說銀行卡號,4個數字一組,做分割處理,再設置給input框輸出展示。

Reactjs還提供一種操作DOM獲取input的輸入值的方式,即使用ref。

首先在構造函數中定義ref

 constructor(props){
       super(props);
       this.state = {searchVal:"",isSearchShow:true,color:25};
       this.onSearchClick = this.onSearchClick.bind(this);
       this.handleChange = this.handleChange.bind(this); 
       //定義ref
       this.input = React.createRef();
    }

然後設置input的ref屬性

 return(
            <div style={{width:300,margin:10}}>
                <input type="text" style={{width:150}} onChange={this.handleChange} ref={this.input} value={this.state.searchVal}></input>&nbsp;&nbsp;
               <button onClick={this.onSearchClick} >搜索</button>
            </div>
        )

就可以使用ref獲取input的對象句柄,比如handleChange通過ref的方式獲取input的值

handleChange(e){
    this.setState({searchVal:this.input.current.value});
    }

使用的時候別忘了current關鍵字。此時input可以通過ref進行DOM操作,稱爲非受控組件。雖然這種模式打破了數據驅動view的原則,但是有時候還是比較方便的,比如獲取滾動條的高度。

在Vue中可有類似的做法,通過自定義的指令操控DOM節點。

六、總結

本章節介紹了Reactjs的事件,條件,列表,表單的基本用法,並與vue進行了簡單的比較。

事件的隱形event參數的使用,阻止默認事件以及冒泡,傳遞參數。

條件的三目運算,&&符,以及if...else條件的使用

列表使用map遍歷,以及key的原理。

表單的兩種不同的獲取對象值的方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章