看過一些文章介紹react元件之間傳值,無外乎以下幾種情況:父子元件之間互相傳值,兄弟節點之間傳值。最常見的就是父子元件,做法也很簡單:就是在父元件中直接通過props屬性的方式将函數或者值傳給子元件。父元件有變化,相應的值就會展現在子元件中;子元件有動作發生,則調用函數,函數是父元件傳遞過來的,父元件就能擷取子元件的值。
還有一種情況,就是兄弟節點之間要傳值,這個還比較複雜,因為他們之間沒有直接的交集,但是他們有一個共同的父元件,可以借助父元件傳遞值,是以還是回到了父子元件傳值上。
這裡有個需求:有一個菜單清單,點選菜單會選擇相應的選項,有一個标簽頁選項,點選标簽可以切換選項。菜單和标簽頁可以産生一種關聯,他們是兩個兄弟節點,現在需要考慮的是選擇菜單,标簽頁也跟着動,标簽頁選擇的時候,菜單也跟着響應,這個示例可以很好的示範多種情況傳值,效果如下所示:
代碼部分:
import React,{useState} from 'react'
import {Tabs,Menu,Divider} from 'antd'
import 'antd/dist/antd.css'
const {TabPane} = Tabs
const MenuList = (props)=>{
const {current,setCurrent} = props;
const handleClick = e =>{
setCurrent(e.key)
}
return <Menu onClick = {handleClick} selectedKeys = {current} >
<Menu.Item key="a">aaa</Menu.Item>
<Menu.Item key="b">bbb</Menu.Item>
<Menu.Item key="c">ccc</Menu.Item>
</Menu>
}
const TabComponents = (props)=>{
const {activeKey,setActiveKey} = props;
const changeTabHandler = (key)=>{
setActiveKey(key)
}
return <Tabs activeKey={activeKey} onTabClick = {changeTabHandler}>
<TabPane tab="aaa" key="a">this is aaa</TabPane>
<TabPane tab="bbb" key="b">this is bbb</TabPane>
<TabPane tab="ccc" key="c">this is ccc</TabPane>
</Tabs>
}
const MyTabs = ()=>{
const [activeKey,setActiveKey] = useState('a')
return <div>
<MenuList setCurrent = {setActiveKey} current={activeKey}/>
<Divider/>
<TabComponents activeKey = {activeKey} setActiveKey = {setActiveKey}/>
</div>
}
export default MyTabs
這裡使用了antd中的Menu和Tabs控件形成MenuList和TabComponents,他們組成一個新的元件MyTabs,在父元件中,我們通過useState函數定義了一個變量和設定變量的方法,分别對應activeKey,setActiveKey,最終将他們都傳入MenuList和TabComponents中。
這種做法,達到了關聯的效果,卻破壞了Tabs預設的onTabClick事件,本來Tabs的切換是根據使用者點選來做切換标簽頁,但是現在變成了既可以手動也可以自動,手動就是,使用者點選菜單選項,通過外部傳入值,切換tab,這裡是通過改變預設值activeKey的方式實作,如果我們不添加onTabClick函數響應,那麼使用者點選标簽頁會失效。
==========================================================================
還有一種做法,可以通用,不僅可以父子之間,還可以是兄弟節點之間傳值,就是釋出訂閱模式。這種方式,不需要父節點來維護值的狀态,是以也不需要通過props屬性傳值,直接在需要傳值的元件之間來通過釋出訂閱即可。元件内部自己維護狀态的改變,而且這種方式也是可以雙向的傳值。
先上效果圖,跟上面沒什麼兩樣:
代碼需要增加一個簡單的釋出訂閱實作:
EventEmmiter.js
const EventEmmiter = {
_events:{},
dispatch:function(event,data){
if(!this._events[event])
return;
for(var i=0;i<this._events[event].length;i++)
{
this._events[event][i](data);
}
},
subscribe:function(event,callback){
if(!this._events[event])
this._events[event] = []
this._events[event].push(callback)
}
}
export default EventEmmiter
PubSub.js
import React,{useState} from 'react'
import {Menu,Tabs,Divider} from 'antd'
import EventEmmiter from '../utils/EventEmmiter'
import 'antd/dist/antd.css'
const {TabPane} = Tabs
const MenuList = (props)=>{
const [current,setCurrent] = useState('a');
const clickHandler = (e)=>{
EventEmmiter.dispatch('changeTab',e.key)
setCurrent(e.key)
}
EventEmmiter.subscribe('changeMenu',(key)=>{
setCurrent(key)
})
return <Menu onClick = {clickHandler} selectedKeys = {current}>
<Menu.Item key="a">aaa</Menu.Item>
<Menu.Item key="b">bbb</Menu.Item>
<Menu.Item key="c">ccc</Menu.Item>
</Menu>
}
const TabComponent = (props)=>{
const [current,setCurrent] = useState('a');
const clickHandler = (e)=>{
EventEmmiter.dispatch('changeMenu',e)
setCurrent(e)
}
EventEmmiter.subscribe('changeTab',(key)=>{
setCurrent(key)
})
return <Tabs activeKey={current} onTabClick = {clickHandler}>
<TabPane key="a" tab="xxx">xxx this is aaa.</TabPane>
<TabPane key="b" tab="yyy">yyy this is bbb.</TabPane>
<TabPane key="c" tab="zzz">zzz this is ccc.</TabPane>
</Tabs>
}
const PubSub = ()=>{
return <div>
<MenuList/>
<Divider/>
<TabComponent/>
</div>
}
export default PubSub
這種方式比起前面,代碼量雖然有增加,但是它卻是一種通用的做法,不用考慮元件的關系。釋出訂閱是分開的,他們各自作用在不同的元件裡面,是成對出現的。