React组件设计模式组合组件

24次阅读

共计 1893 个字符,预计需要花费 5 分钟才能阅读完成。

这种模式本质上解决的是组件之间传值的问题。但是它对于传值以及一些内部操控的逻辑封装得更严密。

场景:希望减少上下级组件之间 props 的传递,简单来说就是不用传做显式地传值,来达到组件之间相互通信的目的

举例来说,某些界面中应该有 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>

这样的缺点很明显:

  • 每次使用 TabItem 都要传递一堆 props
  • 每增加一个新的 TabItem,都要增加对应的 props
  • 如果要增加 TabItem,就要去修改 Tabs 的 JSX 代码

但是,组件之间的交互我们又不希望通过 props 或者 context 来实现。希望用法如下面一样简洁。

    <Tabs>
      <TabItem> 第一 </TabItem>
      <TabItem> 第二 </TabItem>
      <TabItem> 第三 </TabItem>
    </Tabs>

组件之间通过隐秘的方式进行通信,但这里的隐秘实际上是对 props 的操作在一个地方进行管理。


实现

明白了要实现的交互,和代码层面要实现的效果,就可以开始动手了。

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
  • React.cloneElement

使用 React.Children.map 来对 props.children 进行遍历。

React.cloneElement 可以复制某个元素,第一个参数是被复制的元素,第二个参数我们可以把想传入的 props 加进去,也就是这个时机,
我们将 active 和 onTabClick 传入。实现最终效果。

总结

这种模式比较好的把复杂逻辑完全封装起来了,抽象程度更好,比较适合开发组件开发者。针对 props 的扩展性也比较好,对于使用组件的开发者来说,也比较友好。

正文完
 0