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 ...
随机推荐
- flask based on tornado
from flask import Flask from tornado.wsgi import WSGIContainer from tornado.httpserver import HTTPSe ...
- MyBatis笔记----mybatis分页
mybatis版本3.4以下 结构 spring-mvc.xml <?xml version="1.0" encoding="UTF-8"?> &l ...
- JSF中run项目时候Tomcat8启动不了的一种方法
把另一个博客内容迁移到这 我的问题是Tomcat是可以启动的 但是run那个jsp的时候 七月 10, 2016 3:14:54 下午 org.apache.tomcat.util.digester. ...
- [20181015]为什么是3秒.txt
[20181015]为什么是3秒.txt --//以前测试:连接http://blog.itpub.net/267265/viewspace-2144765/=>为什么是12秒.txt.--// ...
- C# 函数1 (函数的定义)
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 na ...
- 数据挖掘---Matplotib的学习
什么是matplotlib mat - matrix 矩阵 二维数据 - 二维图表 plot - 画图 lib - libra ...
- win10安装nodejs遇到提示错误代码2503怎么办
我们在安装某个软件的时候,最闹心的就是遇到提示安装失败或错误,比如win10系统在安装nodejs遇到提示错误代码2503,遇见这个问题也不要慌张,今天小编就来告诉大家怎么解决这个问题. 1.打开智能 ...
- 【Git】Git pull 强制覆盖本地文件
git fetch --all git reset --hard origin/master git pull 备注: git fetch 只是下载远程的库的内容,不做任何的合并 git reset ...
- 在Eclipse中创建maven项目出现的环境警告 j2se-1.5
Build path specifies execution environment J2SE-1.5. There are no JREs installed in the workspace th ...
- C#字节数组与字符串转换
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...