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最重要的概念了state
,state
是一個Javascript的Object,它是用來表示元件的當前狀態的,如果用TabsControl
舉例的話,它的state
可以是當前處於啟用狀態的標籤頁編號(當然,如果你想的話也可以儲存標籤頁的內容)。
React提供了一個方法setState()
讓你可以改變state
的值。每次呼叫setState()
都會觸發元件的render()
,也就是說會把元件所代表的DOM更新到state
所代表的狀態。
所以實現切換的關鍵如下:
-
state
儲存當前處於啟用狀態的標籤頁的編號 -
點選標題的時候呼叫
setState()
更新啟用的標籤頁編號 -
render()
的時候,在遍歷this.props.children
的時候把編號與state
中編號一致的元素標記為active
-
用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.
接下來的改進
實現可以滑動的標籤頁