Element(React)源碼分析系列4--Radio組件

前言

學習就比如是座大山,人們沿着不一樣的路爬山,分享着本身看到的風景。你不必定能看到別人看到的風景,體會到別人的心情。只有本身去爬山,才能看到不同的風景,體會更加深入。一千個讀者就有一千個哈姆雷特,可是莎士比亞心中的哈姆雷特確定只有一個。就比如element源碼只有一個,但每一個人看到的都是不同的風景。element源碼解讀是一個系列,每個組件細膩的背後都能看到前端工程師付出的心血,本篇帶來的是Element源碼分析系列4-Radio(單選框)css

簡介

單選框這個組件看似簡單,實則知識點衆多,較爲複雜,若是寫一個html的原生單選框,那確實很簡單,可是封裝一個完整的單選組件就不那麼簡單了。element團隊在整個radio組件的設計和構思真可謂十分的細緻,沒有一行多餘的代碼。從基礎的css到選擇邏輯都無不彰顯其巧奪天工的思想。html

劃重點

一、如何隱藏原始radio標籤默認樣式

咱們都知道原生的radio標籤很醜,樣式在各個瀏覽器不統一,做爲一個組件不可能就這樣屈服,何況人這種物種老是但願統駕馭全部的物質之上,包括代碼。因此必須本身實現全部radio按鈕的樣式,那麼如何本身實現一個可控制的radio標籤呢?看element是怎麼實現的。 前端

radio
看圖不難分析出radio組件的html樣式,整個大盒子被一個laber標籤包裹着,laber裏分爲兩個span,第一個span是用來展現選擇按鈕的,第二個span是來描述選項的。第一個span裏應該包括input(用來作radio)和span(隱藏真正的input)兩個標籤。其相關的html結構以下:

<label className='el-radio'>
    <span>
      <span className="el-radio__inner"></span>
      <input 
        type="radio"
        className="el-radio__original"
      />
    </span>
    <span className="el-radio__label">
      {children || value}
    </span>
</label>
複製代碼

問題一:如何作到隱藏原始radio標籤默認樣式?做者巧用opacity:0,真正的input透明度爲0,且是絕對定位脫離文檔流,所以不佔空間且咱們看不到,注意不是display:none或者visibility:hidden,若是是none或者hidden的話則沒法觸發鼠標點擊了,因此只有opacity:0才能達到目的.react

.el-radio__original {
    opacity: 0;
    outline: 0;
    position: absolute;
    z-index: -1;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    margin: 0;
}
複製代碼

二、如何點擊選擇按鈕展現不一樣的狀態

如何作到點擊旋鈕顯示不一樣的狀態。經過radio標籤可分析出,點擊便可以理解爲一個事件(這裏用onChange表示)。經過點擊事件傳遞一個props值(checked)顯示不一樣的狀態。因此在input上必定會有一個事件和一個所要傳遞的狀態。git

<input 
    type="radio"
    className="el-radio__original"
    checked={checked}  // 傳遞的狀態
    onChange={this.onChange.bind(this)}   // 事件
 />
複製代碼

那麼接下來就是定義事件了,其核心代碼爲:github

constructor(props) {
    super(props);
    this.state = {
      checked: this.getChecked(props)  // 定義一個state,受props影響
    };
  }
  componentWillReceiveProps(props) {
    const checked = this.getChecked(props);  
    if (this.state.checked !== checked) {   // 維持每次點擊時只選中一個
      this.setState({ checked });
    }
  }
  getChecked(props) {
    return  Boolean(props.checked)  // 根據傳遞的props,輸出true/false
  }
  onChange(e) {
    const checked = e.target.checked;  // 定義被點擊項的checked爲true
    if (checked) {
      if (this.props.onChange) {
        this.props.onChange(this.props.value);  // 向外暴露一個onChange事件並攜帶value值
      }
    }
    this.setState({ checked }); // 更新被點擊項的state爲true
  }
複製代碼

若是你對上述描述的仍是雨裏霧裏,那麼下面這張圖能夠更好的幫組你理解element-radio標籤事件的邏輯: 數組

從圖中咱們能夠看出,radio是如何作到點擊改變狀態,而且又如何維護每次點擊完後radio的狀態。

  • 傳入checked做爲props決定每一個radio的狀態
  • 根據傳入的props,定義一個內部的state,跟props的checked保持一致
  • 點擊事件改變當前的radio的狀態,更新state中的checked,而且向外傳遞一個value
  • 外面接受到傳來的value,從新定義props
  • 經過生命週期函數更新內部的state,讓其保持每一個radio一致

三、Radio.Group如何作到單選框組

單選框組故名思議是將全部的radio包裹一層,由最外層原始事件來決定每一個radio的狀態。故須要用到React的兩個API:React.Children.map、React.cloneElement瀏覽器

技術擴展:bash

  • React.Children 提供了用於處理 this.props.children 不透明數據結構的實用方法。

React.Children.map(children, function[(thisArg)])前端工程師

在 children 裏的每一個直接子節點上調用一個函數,並將 this 設置爲 thisArg。若是 children 是一個數組,它將被遍歷併爲數組中的每一個子節點調用該函數。若是子節點爲 null 或是 undefined,則此方法將返回 null 或是 undefined,而不會返回數組。

  • React.cloneElement: 以 element 元素爲樣板克隆並返回新的 React 元素。返回元素的 props 是將新的 props 與原始元素的 props 淺層合併後的結果。

React.cloneElement(element, [props], [...children])

先了解這兩個API,在來看看element中Radio.Group的源碼就很簡單來。

<div ref='RadioGroup' className='el-radio-group'>
    {
      React.Children.map(this.props.children, element => {
        if (!element) {
          return null
        }

        return React.cloneElement(element, Object.assign({}, element.props, {
          onChange: this.onChange.bind(this),
          value: this.props.value,
        }))
      })
    }
</div>
複製代碼

經過遍歷全部的radio組件,克隆出相應的radio組件,併爲其附上radio的一些屬性,包括方法和value。

結語

縱觀整個radio的設計和實現,每一個設計的過程都十分的微妙,尤爲是radio如何更新狀態,在平時業務中,咱們也常常會遇到這種需求,可是真正的實現起來卻沒有這麼簡潔。源碼的學習或多或少可讓咱們加強本身的代碼能力和業務能力。那麼跟隨個人腳步,帶你一步一步解析整個element源碼的奇思妙想。山再也不高,有仙則靈;水不在深,有心則成。後續將推出更多源碼解析文章。源碼請👇這裏element-radio源碼

相關文章
相關標籤/搜索