天天看點

react元件之間傳值

    看過一些文章介紹react元件之間傳值,無外乎以下幾種情況:父子元件之間互相傳值,兄弟節點之間傳值。最常見的就是父子元件,做法也很簡單:就是在父元件中直接通過props屬性的方式将函數或者值傳給子元件。父元件有變化,相應的值就會展現在子元件中;子元件有動作發生,則調用函數,函數是父元件傳遞過來的,父元件就能擷取子元件的值。

    還有一種情況,就是兄弟節點之間要傳值,這個還比較複雜,因為他們之間沒有直接的交集,但是他們有一個共同的父元件,可以借助父元件傳遞值,是以還是回到了父子元件傳值上。

    這裡有個需求:有一個菜單清單,點選菜單會選擇相應的選項,有一個标簽頁選項,點選标簽可以切換選項。菜單和标簽頁可以産生一種關聯,他們是兩個兄弟節點,現在需要考慮的是選擇菜單,标簽頁也跟着動,标簽頁選擇的時候,菜單也跟着響應,這個示例可以很好的示範多種情況傳值,效果如下所示:

react元件之間傳值

    代碼部分:

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屬性傳值,直接在需要傳值的元件之間來通過釋出訂閱即可。元件内部自己維護狀态的改變,而且這種方式也是可以雙向的傳值。

    先上效果圖,跟上面沒什麼兩樣:

react元件之間傳值

    代碼需要增加一個簡單的釋出訂閱實作:

    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
           

     這種方式比起前面,代碼量雖然有增加,但是它卻是一種通用的做法,不用考慮元件的關系。釋出訂閱是分開的,他們各自作用在不同的元件裡面,是成對出現的。