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 ...
随机推荐
- mysql学习之完整的select语句
本文内容: 完整语法 去重选项 字段别名 数据源 where group by having order by limit 首发日期:2018-04-11 完整语法: 先给一下完整的语法,后面将逐一来 ...
- 使用VSTS的Git进行版本控制(二)——提交保存工作
使用VSTS的Git进行版本控制(二)--提交保存工作 当对文件进行更改时,Git将在本地仓库中记录更改.可以通过选择变更来提交的对应更改.提交总是针对本地的Git仓库,因此不必担心提交是完美的,或者 ...
- NFV一种提高进程消息高可用性的方法
1.背景及概述 1.1 背景 在做NFV的过程中,由于控制面进程被放置到不同虚拟机中,中间可能跨越路由器,因此期间网络有可能震荡,这种情况下保证高可用性就必须有保护机制,本文正是在这种背景下的考虑. ...
- IDEA实用插件Lombok
Lombok Lombok是一个可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法.通常,我们所定义的对象和be ...
- SQL Server基础之登陆触发器
虽然同表级(DML)触发器和库级(DDL)触发器共顶着一个帽子,但登陆触发器与二者有本质区别.无论表级还是库级,都是用来进行数据管理的,而登陆触发器是纯粹的安全工具. 登陆触发器只响应LOGON事件, ...
- perl语言中的.pm文件和.pl文件区别
perl...呵呵呵 按照惯例,.pm 应该保存 Perl Module,也就是 Perl 模块.例如 Socket.pm.pl 应该保存 Perl Library,也就是 Perl 库文件.例如 p ...
- Linux 小知识翻译 - 「代理服务器」
这回聊聊「代理服务器」. 在公司里,不通过代理服务器无法连接互联网的,由于代理服务器的原因,有些服务的使用是受到限制的. 有人可能会觉得为什么会存在这种东西?(这里指代理服务器) Proxy本来的意思 ...
- Pandas 处理丢失数据
处理丢失数据 import pandas as pd from pandas import Series, DataFrame import numpy as np 有两种丢失数据: 1. None ...
- WPF自定义控件(三)の扩展控件
扩展控件,顾名思义就是对已有的控件进行扩展,一般继承于已有的原生控件,不排除继承于自定义的控件,不过这样做意义不大,因为既然都自定义了,为什么不一步到位呢,有些不同的需求也可以通过此来完成,不过类似于 ...
- 十大PHP程序员必备工具
十大PHP程序员必备工具 1.Notepad++ 总结来说就是小而精,7.4版本的软件包只有2.9M,比一般的IDE小数十倍,但是Notepad++的功能确是很全面的,代码高亮,语法折叠,宏功能,内置 ...