React组件设计模式-Provider-Consumer设计模式
这种模式本质上解决的是组件之间传值的问题。可是它对于传值以及一些内部操控的逻辑封装得更严密。bash
场景:但愿减小上下级组件之间props的传递,简单来讲就是不用传作显式地传值,来达到组件之间相互通讯的目的ide
举例来讲,某些界面中应该有Tabs这样的组件,由Tab和TabItem组成,点击每一个TabItem,该TabItem会高亮, 那么Tab和TabItem天然要进行沟通。很天然的写法是像下面这样函数
<TabItem active={true} onClick={this.onClick}>One</TabItem>
<TabItem active={false} onClick={this.onClick}>Two</TabItem>
<TabItem active={false} onClick={this.onClick}>Three</TabItem>
复制代码
这样的缺点很明显:post
可是,组件之间的交互咱们又不但愿经过props或者context来实现。但愿用法以下面同样简洁。ui
<Tabs>
<TabItem>第一</TabItem>
<TabItem>第二</TabItem>
<TabItem>第三</TabItem>
</Tabs>
复制代码
组件之间经过隐秘的方式进行通讯,但这里的隐秘其实是对props的操做在一个地方进行管理。this
明白了要实现的交互,和代码层面要实现的效果,就能够开始动手了。spa
TabItem组件有两个关键的props: active(代表当前是否应高亮),onTabClick(本身被点击时调用的回调函数), TabItem因为是每一个Tab页面的容器,它只负责把props.children渲染出来,因此用函数式组件便可。设计
export const TabItem = props => {
const { active, onTabClick, children } = props
const style = {
color: active ? 'red' : 'green',
cursor: 'pointer'
}
return <>
<h1 style={style} onClick={onTabClick}>
{children}
</h1>
</>
}
复制代码
咱们再来回顾一下想到达到的效果:
<Tabs>
<TabItem>第一</TabItem>
<TabItem>第二</TabItem>
<TabItem>第三</TabItem>
</Tabs>
复制代码
使用组件时要避免传递props的缺点,那么在哪里传递呢?天然是是Tabs组件。但上面并无传入props啊。 Tabs 虽然能够访问到props里边的children,可是到手的children已是现成的若是直接改它的话,会出问题。 不能够直接改children的话,咱们就把children复制一份,而后改这个复制过来的children,再渲染出去,就ok啦!
下面来看Tabs的实现:
class Tabs extends React.Component {
state={
activeIndex: 0
}
render() {
const { activeIndex } = this.state
const newChildren = React.Children.map(this.props.children, (child, index) => {
if (child.type) {
// 复制并修改children
return React.cloneElement(child, {
active: activeIndex === index,
onTabClick: () => this.setState({activeIndex: index})
})
} else {
return child
}
})
return <div className="tabs">
{newChildren}
</div>
}
}
复制代码
这里须要用到React不经常使用的api:
使用React.Children.map
来对props.children进行遍历。
而React.cloneElement
能够复制某个元素,第一个参数是被复制的元素,第二个参数咱们能够把想传入的props加进去,也就是这个时机, 咱们将active和onTabClick传入。实现最终效果。
这种模式比较好的把复杂逻辑彻底封装起来了,抽象程度更好,比较适合开发组件开发者。针对props的扩展性也比较好,对于使用组件的开发者来讲,也比较友好。