天天看点

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
           

     这种方式比起前面,代码量虽然有增加,但是它却是一种通用的做法,不用考虑组件的关系。发布订阅是分开的,他们各自作用在不同的组件里面,是成对出现的。