1. 程式人生 > >react實現選項卡

react實現選項卡

一、首先是Showcase

效果  傳送門

二、如何實現

既然用React寫,那麼它就必然是一個元件,首先考慮你怎麼使用這個元件,也就是這個元件的介面是怎麼樣的。

<TabsControl>
  <Tab name="red">
    <div className="red"/>
  </Tab>
  <Tab name="blue">
    <div className="blue"/>
  </Tab>
  <Tab name="yellow">
    <div className="yellow"/>
  </Tab>
</TabsControl>

這個TabsControl作為父元件,它來控制Tab的如何切換,Tab是用來包裹真正要顯示的內容的,它的name屬性是這個標籤頁的名字,會被顯示在標籤頁的標題欄上。

三、建立基本元素

按照之前的想法,我們用Tab定義了很多個標籤頁,我們需要根據這些定義生成標籤頁的標題欄和內容。

1. 遍歷this.props.children動態生成標題欄

this.props.children是React內建的一個屬性,用來獲取元件的子元素。因為子元素有可能是Object或者Array,所以React提供了一些處理children的輔助方法用來遍歷:React.Children.map

那麼動態生成標題的程式碼可能是這樣子的:

React.Children.map(this.props.children, (element, index) => {
    return (<div className="tab-title-item">{element.props.name}</div>)

2. 再用同樣方法生成標籤頁內容

React.Children.map(this.props.children, element => {
    return (element)
})

組合起來就是TabsControl的實現:

let TabsControl = React.createClass({
  render: function () {
    let that = this;
    let {baseWidth} = this.props;
    let childrenLength = this.props.children.length;
    return (
      <div>
        <nav className="tab-title-items">
          {React.Children.map(this.props.children, (element, index) => {
            return (<div className="tab-title-item">{element.props.name}</div>)
          })}
        </nav>
        <div className="tab-content-items">
          {React.Children.map(this.props.children, element => {
            return (element)
          })}
        </div>
      </div>
    )
  }
});

加上一些css就能看到一個標籤頁的雛形了。

三、實現標籤頁切換

這裡要出現React最重要的概念了statestate是一個Javascript的Object,它是用來表示元件的當前狀態的,如果用TabsControl舉例的話,它的state可以是當前處於啟用狀態的標籤頁編號(當然,如果你想的話也可以儲存標籤頁的內容)。
React提供了一個方法setState()讓你可以改變state的值。每次呼叫setState()都會觸發元件的render(),也就是說會把元件所代表的DOM更新到state所代表的狀態。

所以實現切換的關鍵如下:

  1. state儲存當前處於啟用狀態的標籤頁的編號

  2. 點選標題的時候呼叫setState()更新啟用的標籤頁編號

  3. render()的時候,在遍歷this.props.children的時候把編號與state中編號一致的元素標記為active

  4. 用css將非active的元素隱藏起來

所以程式碼是這樣的:

let TabsControl = React.createClass({
  getInitialState: function(){
    return {currentIndex: 0}
  },
  
  getTitleItemCssClasses: function(index){
    return index === this.state.currentIndex ? "tab-title-item active" : "tab-title-item";
  },
  
  getContentItemCssClasses: function(index){
    return index === this.state.currentIndex ? "tab-content-item active" : "tab-content-item";
  },
  
  render: function(){
    let that = this;
    let {baseWidth} = this.props;
    let childrenLength = this.props.children.length;
    return (
      <div>
        <nav className="tab-title-items">
          {React.Children.map(this.props.children, (element, index) => {
            return (<div onClick={() => {this.setState({currentIndex: index})}} className={that.getTitleItemCssClasses(index)}>{element.props.name}</div>)
          })}
        </nav>
        <div className="tab-content-items">
          {React.Children.map(this.props.children, (element, index) => {
            return (<div className={that.getContentItemCssClasses(index)}>{element}</div>)
          })}  
        </div>
      </div>
    )
  }
});

getInitialState:是元件的初始化狀態,預設是第一個標籤頁處於啟用狀態。
getTitleItemCssClasses:判斷當前標籤和state中儲存的標籤編號是否一直,是則標識為active
getContentItemCssClasses:同上。
onClick={() => {this.setState({currentIndex: index})}}:標籤頁標題綁定了點選事件,每次點選都會更新state儲存的標籤頁編號,然後觸發render()方法重繪標籤頁。

四、總結

上面一系列的操作最終的結果都需要用render()來反應出來,所以關鍵點是如何在render()中使用state來動態生成DOM.

接下來的改進

實現可以滑動的標籤頁

原文網址:https://segmentfault.com/a/1190000004200481