React中嵌套组件与被嵌套组件的通信
前言
在React项目的开发中经常会遇到这样一个场景:嵌套组件与被嵌套组件的通信。
比如Tab组件啊,或者下拉框组件。
场景
这里应用一个最简单的Tab组件来呈现这个场景。
import React, { Component, PropTypes } from 'react'
class Tab extends Component {
static propTypes = {
children: PropTypes.node
}
render() {
return (
<ul>
{this.props.children}
</ul>
)
}
}
class TabItem extends Component {
static propTypes = {
name: PropTypes.string,
active: PropTypes.bool,
onClick: PropTypes.func
}
handleClick = () => {
this.props.onClick(this.props.name)
}
render() {
return (
<li onClick={this.handleClick} className={this.props.active ? 'active' : 'noActive'}>
{this.props.name}
</li>
)
}
}
export default class Area extends Component {
state = {
activeName: ''
}
handleClick = (name) => {
this.setState({
activeName: name
})
}
render() {
return (
<Tab>
{['武汉', '上海', '北京'].map((item) => <TabItem onClick={this.handleClick} active={this.state.activeName === item} name={item} />)}
</Tab>
)
}
}
这里有Tab,TabItem和Area三个组件,其中Tab为嵌套组件,TabItem为被嵌套组件,Area为使用它们的组件。
在上述场景中,点击哪个TabItem项时,就将这个TabItem项激活。
以上方案算是嵌套组件最常用的方案了。
需求的变更与缺陷的暴露
在上述场景下应用上述方案是没有问题的,但是我们通常用的Tab没有这么简单,比如当点击武汉这个TabItem时,武汉地区的美食也要展示出来。
这种场景下就需要修改TabItem组件为:
class TabItem extends Component {
static propTypes = {
name: PropTypes.string,
active: PropTypes.bool,
onClick: PropTypes.func,
children: PropTypes.node
}
handleClick = () => {
this.props.onClick(this.props.name)
}
render() {
return (
<li onClick={this.handleClick} className={this.props.active ? 'active' : 'noActive'}>
<span className='switchBtn'>{this.props.name}</span>
<div className={this.props.active ? 'show' : 'hide'}>
{this.props.children}
</div>
</li>
)
}
}
然后沿用上述方案,那么就需要改变Area组件为:
export default class Area extends Component {
state = {
activeName: ''
}
handleClick = (name) => {
this.setState({
activeName: name
})
}
render() {
return (
<Tab>
<TabItem onClick={this.handleClick} active={this.state.activeName === '武汉'} name={'武汉'} >
武汉的美食,这里有一大堆jsx代码
</TabItem>
<TabItem onClick={this.handleClick} active={this.state.activeName === '上海'} name={'上海'} >
武汉的美食,这里有一大堆jsx代码
</TabItem>
<TabItem onClick={this.handleClick} active={this.state.activeName === '北京'} name={'北京'} >
武汉的美食,这里有一大堆jsx代码
</TabItem>
</Tab>
)
}
}
这里的Area使用TabItem的时候已经没办法用 数组+map 的形式去写了。
因为这里有大量的jsx在这里,如果那样去写,代码的可读性将会非常糟糕。
那么用上面的写法写的时候,就会出现一个问题,就是onClick在不断重复,active的判断也在不断重复。
尝试掩盖active判断重复的问题
这个比较容易,修改代码如下:
class TabItem extends Component {
static propTypes = {
name: PropTypes.string,
activeName: PropTypes.string,
onClick: PropTypes.func,
children: PropTypes.node
}
handleClick = () => {
this.props.onClick(this.props.name)
}
render() {
return (
<li onClick={this.handleClick} className={this.props.activeName === this.props.name ? 'active' : 'noActive'}>
<span className='switchBtn'>{this.props.name}</span>
<div className={this.props.active ? 'show' : 'hide'}>
{this.props.children}
</div>
</li>
)
}
}
export default class Area extends Component {
state = {
activeName: ''
}
handleClick = (name) => {
this.setState({
activeName: name
})
}
render() {
return (
<Tab>
<TabItem onClick={this.handleClick} activeName={this.state.activeName} name={'武汉'} >
武汉的美食,这里有一大堆jsx代码
</TabItem>
<TabItem onClick={this.handleClick} activeName={this.state.activeName} name={'上海'} >
武汉的美食,这里有一大堆jsx代码
</TabItem>
<TabItem onClick={this.handleClick} activeName={this.state.activeName} name={'北京'} >
武汉的美食,这里有一大堆jsx代码
</TabItem>
</Tab>
)
}
}
尝试掩盖onClick不断重复的问题
想要onClick不重复,那么就不能将其写在TabItem上,而是应该写在Tab上。
那么这个地方就得用到事件冒泡的机制。
将onClick写在Tab上,然后根据捕获的事件消息,获取target的class是否为switchBtn,然后得到target的text。
再将这个text赋值为activeName。
并且你还得期望点击的switchBtn的内的结构不那么复杂,最好是就只有一个文本。
如果需求还要给Tab项的切换按钮每个都加上图标,那么你还得看这个事件的target是不是这个图标。那么又需要做更多的处理了。
想一想就觉得麻烦。
一般在这种情况下,脑子里唯一的想法就是,就这样吧,这个onClick重复就重复吧,没什么大不了的。
连我自己都懒得写这部分代码了。
嵌套组件与被嵌套组件的通信:React.Children与React.cloneElement
实际上要解决上面的问题,只需要一个东西就好了,那就是嵌套组件能传递值给被嵌套组件的props,比如onClick。
那么先上一份代码吧。
class TabItem extends Component {
static propTypes = {
name: PropTypes.string,
activeName: PropTypes.string,
onClick: PropTypes.func,
children: PropTypes.node
}
handleClick = () => {
this.props.onClick(this.props.name)
}
render() {
return (
<li onClick={this.handleClick} className={this.props.activeName === this.props.name ? 'active' : 'noActive'}>
<span className='switchBtn'>{this.props.name}</span>
<div className={this.props.active ? 'show' : 'hide'}>
{this.props.children}
</div>
</li>
)
}
}
class Tab extends Component {
static propTypes = {
children: PropTypes.node,
onClickItem: PropTypes.func,
activeName: PropTypes.string
}
render() {
return (
<ul>
{
React.Children.map(this.props.children,(child)=>{
if (child.type === TabItem) {
return React.cloneElement(child, {
// 把父组件的props.name赋值给每个子组件(父组件传值给子组件)
activeName: this.props.activeName,
// 父组件的方法挂载到props.onClick上,以便子组件内部通过props调用
onClick: this.props.onClickItem
})
} else {
return child
}
})
}
</ul>
)
}
}
export default class Area extends Component {
state = {
activeName: ''
}
handleClick = (name) => {
this.setState({
activeName: name
})
}
render() {
return (
<Tab activeName={this.state.activeName} onClick={this.handleClick} >
<TabItem name={'武汉'} >
武汉的美食,这里有一大堆jsx代码
</TabItem>
<TabItem name={'上海'} >
武汉的美食,这里有一大堆jsx代码
</TabItem>
<TabItem name={'北京'} >
武汉的美食,这里有一大堆jsx代码
</TabItem>
</Tab>
)
}
}
通过这种方式,我们发现在使用Tab和TabItem时会变得非常简单。
那么接下来让我们介绍一下解决嵌套组件通信这个问题的关键:React.Children.map和React.cloneElement。
React.Children
React.Children是专门用来处理this.props.children这个东西的工具。
通常props.children可以是任何变量类型:数组、对象、文本或者其他的一些类型,但是我们这里使用
React.Children.map(this.props.children,(child)=>{
// ***
})
无论this.props.children的类型是什么都不会报错。
这里只是用了React.children的map函数,实际上它还有foreach,count以及only的玩法。
foreach就不解释了,很容易理解是干嘛的。
count就是得到被嵌套组件的数量。
only就是返回被嵌套的组件,并且只能有一个被嵌套的组件,否则会抛异常。
React.cloneElement
先看下面这段代码
const child= <Child value={1} />
const newChild=React.cloneElement(child,{
name:'额外的props'
},'123')
newChild的值为:
<Child value={1} name='额外的props' >
123
</Child>
可以很明显看到,React.cloneElement的就相当克隆一个组件,然后可以传给它额外的props和children。
总结
对于简单的嵌套组件用最开始的方法其实已经够了。
但是对于复杂的嵌套组件为了更好更方便的使用,往往需要与被嵌套的组件进行通信。
而我们可以使用React.Children和React.cloneElement来解决这个问题。
React中嵌套组件与被嵌套组件的通信的更多相关文章
- react中直接调用子组件的方法(非props方式)
我们都知道在 react中,若要在父组件调用子组件的方法,通常我们会采用在父组件定义一个方法,作为props转给子组件,然后执行该方法,可以获取到子组件传回的参数以得到我们的目的. 显而易见,这个执行 ...
- React躬行记(9)——组件通信
根据组件之间的嵌套关系(即层级关系)可分为4种通信方式:父子.兄弟.跨级和无级. 一.父子通信 在React中,数据是自顶向下单向流动的,而父组件通过props向子组件传递需要的信息是组件之间最常见的 ...
- react中的children使用方法
使用过vue的小伙伴都知道vue中有个slot,也就是插槽,作用就是占位,那么再react中可以使用children来替代 父组件 render(){ return( <div> < ...
- React中props与state
以下内容均为个人理解. 1.state: 在react中,state可以看成管理页面状态的集合(实则一个对象而已),库里面的成员均为页面渲染变量,整个页面为一个状态机,当state发生变化时,页面会重 ...
- 整理下react中常见的坑
其实有些也不能算是坑,有些是react的规定,或者是react的模式和平常的js处理的方式不同罢了 1.setState()是异步的this.setState()会调用render方法,但并不会立即改 ...
- 5. React 组件的协同使用 组件嵌套和Mixin
组件是React的核心,构建大型项目时多个组件之间需要进行协同使用.可以从横向和纵向两个角度来实现组件的协同使用,纵向的协同使用就是组件嵌套,横向的协同使用就是Mixin(抽取公共方法 ...
- React组件重构:嵌套+继承 与 高阶组件
前言 在最近做的一个react项目中,遇到了一个比较典型的需要重构的场景:提取两个组件中共同的部分. 最开始通过使用嵌套组件和继承的方式完成了这次重构. 但是后来又用高阶组件重新写了一遍,发现更好一点 ...
- React 三大属性state,props,refs以及组件嵌套的应用
React 三大属性state,props,refs以及组件嵌套的应用 该项目实现了一个简单的表单输入添加列表的内容 代码如下 <!DOCTYPE html> <html> & ...
- 【react】利用prop-types第三方库对组件的props中的变量进行类型检测
1.引言--JavaScript就是一个熊孩子 1.1对于JSer们来说,js是自由的,但同时又有许多让人烦恼的地方.javascript很多时候就是这么一个熊孩子,他很多时候并不会像C和java ...
随机推荐
- [转] Vue生命周期
Vue生命周期 这是Vue文档里关于实例生命周期的解释图 那么下面我们来进行测试一下 <section id="app-8"> {{data}} </sectio ...
- Python minidom模块(DOM写入和解析XML)
一.DOM写XML文件 #导入minidom from xml.dom import minidom # 1.创建DOM树对象 dom=minidom.Document() # 2.创建根节点.每次都 ...
- mysql执行语句提示Table 'performance_schema.session_variables' doesn't exist
用管理员身份cmd进入mysql安装目录bin里,执行 mysql_upgrade -u root -p 如果杀毒软件拦截,添加为信任区
- js 时间戳转时间格式
$("#showbidMessage").append(<span>' + ChangeDateFormat(rows[i].createTime) + '</s ...
- 解决Protege打开owl文件时程序卡死问题
Protege在打开本地owl文件时,程序卡死,而且在终端或是命令行中也没有报错.这是因为存放该本体的文件夹下面有很多其他的文件,只需要创建一个新的文件夹并把owl文件放入其中就可以解决该问题.
- [转]JIRA 7.2.6与Confluence 6.0.3的安装与配置之MS SQL Server版
相关软件版本信息 说明:下方软件可以点击链接,通过百度云盘进行下载. 操作系统:Windows 10(密码:foht)或者Windows Server 2012(密码:lsad): 数据库:SQL S ...
- 2016某知名互联网公司PHP面试题及答案(续)
1 写出mysql中,插入数据,读出数据,更新数据的语句 INSERT INTO 表名 VALUES ("",""): SELECT * FROM 表名:. U ...
- 联想x3650m5服务器安装windows2008R2系统
服务器型号:联想x3650 M5 2U服务器 硬盘:一块300G硬盘 阵列:raid0 系统:windowsserver2008R2系统 安装开始时间:20180930晚上9点 客户手里有window ...
- LeetCode算法题-Excel Sheet Column Title(Java实现)
这是悦乐书的第180次更新,第182篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第39题(顺位题号是168).给定正整数,返回Excel工作表中显示的相应列标题.例如: ...
- Java同步(Synchronization)
前言 线程间的通信主要通过共享对字段的访问和对象引用字段的引用,可能会产生两种错误,线程干扰和内存一致性错误.Java的同步就是防止这些错误,但当多个线程访问同一资源会导致线程执行缓慢,甚至暂停执行. ...