前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >React组件复用的技巧详解

React组件复用的技巧详解

原创
作者头像
夏天的味道123
发布2022-12-07 12:14:42
6250
发布2022-12-07 12:14:42
举报
文章被收录于专栏:前端面试笔记

复用是组件化开发体系的立命之本,可以说组件化的初衷就是为了复用性。但是组件化的复用方式也存在一定的问题,其中拆分粒度就是其中一个绕不开的话题,今天咱们就来讲一讲 React 当中的一个不太常用的 API:cloneElement,他如何帮组我们更好得进行组件拆分。

假如我们有一个Layout组件,那么一般来说这个组件主要接收的就是children,把它放在主要内容的部分,然后组件本身的节点来控制布局,那么这个时候如果我们这个布局包含两个部分呢,比如还有一个header部分,是跟主要内容有明显区分的。

比如:

layout
layout

那么我们这个时候会如何设计这个组件呢?

版本一

代码语言:javascript
复制
function Layout({ header: Header, children }) {
  return (
    <div className='container'>
      <div className='header'>
        <Header />
      </div>
      <div classNmae='content'>{children}</div>
    </div>
  )
}

这应该是我们比较常见的方式,我们通过把具体组件作为Layoutprops传入进来,然后按照组件的写法把它写入到组件渲染内容之中。

我们想要使用这个组件,一般会像下面这样:

代码语言:javascript
复制
function Header() {
  return <h1>Title Here</h1>
}

;<Layout header={Header}>
  <div>content here</div>
</Layout>

那么这样做有什么问题呢?显然是有的,最明显的就是无法在使用Header的时候指定props

如果Headerprops,那么就我们只能硬编码在Layout里面,不能在使用Header组件的地方进行声明,所以如果我们想要复用一个Header组件,我们可能需要再声明一个组件,比如我们给Header组件一个叫做messageprop用来指定显示的文字内容

代码语言:javascript
复制
function Header({ message = 'Title Here' }) {
  return <h1>{message}</h1>
}

那么如果我们想要在不同页面复用这个组件并且显示不同的标题,我们需要这么做:

代码语言:javascript
复制
function BigHeader() {
  return <Header message='The Other Title' />
}

这么做显然在组件较为复杂而且props较多的情况下,也可以达到一定的复用效果,但是追求极致的我们肯定不希望仅仅局限于此。

第二版

那么有没有办法让我们可以在使用时能指定props呢?答案肯定是有的,我们可以将Layoutheader这个prop接收的不是组件本体,而是具体的ReactElement

代码语言:javascript
复制
function Layout({ header, children }) {
  return (
    <div className='container'>
      <div className='header'>{header}</div>
      <div classNmae='content'>{children}</div>
    </div>
  )
}

那么我们在使用的时候就可以非常方便得指定props

代码语言:html
复制
<Layout header={<Header message='The Other Title' />}>
  <div>Content Here</div>
</Layout>

要理解我们可以这么做,首先我们需要弄清楚什么是ReactElement。因为我们大部分时候写React组件的时候用的都是JSX,所以很多同学可能并不知道ReactElement的存在。

其实JSX经过babel翻译之后得到的是如下代码:

代码语言:javascript
复制
// jsx
;<div id='id'>content</div>

// js
React.createElement('div', { id: 'id' }, 'content')

这个函数接收三个参数

  • component具体渲染的组件,包括原生 dom 节点(string)和自定义组件(object)
  • config,包括所有props再加上keyref形成的字典对象
  • children,子节点内容,可以是ReactElementArraystring等内容

最后他返回的是一个叫做ReactElement类型的对象,他会包含后续 React 渲染过程中需要用到的一个节点包含的所有信息,我们的props.children其实就是最典型的ReactElement

所以在上诉例子中,我们传入的header就是一个ReactElement,所以可以直接作为其他节点的children而使用。

同时使用这种方式我们还获得来一个非常大的优势,那就是我们甚至可以重新定义一个组件,就可以直接使用Layout

代码语言:css
复制
<Layout header={<h1>The Other Title</h1>}>
  <div>Content Here</div>
</Layout>

这样同样也是可以行得通的。

那么是否到这里我们就大功告成来呢?NO,NO,NO,我们还是有值得优化的地方。

第三版

试想一下,如果我们的Layout中接收来header是一个节点,但是呢他希望对传入的组件的一些props有强制的要求呢?比如我们的Header组件如果还有另外一个propcolor,用来指定文字内容的显示颜色:

代码语言:javascript
复制
function Header({ message = 'Title Here', color = 'red' }) {
  return <h1 style={{ color }}>{message}</h1>
}

Layout要求所有传入的Header必须颜色是green,显示我们也可以在使用Header组件的时候自己指定这个prop,但是如果我们需要强制指定的prop很多,而且使用Layout的地方也很多,那么明显我们会写很多重复代码,而且如果后面我们需要修改这个要求的时候也会导致多次修改,甚至有些地方忘了修改而导致 bug。那么这时候我们该怎么做呢?

我们可以使用一个 API,这个 API 并不常用,但是在这种场景下,他却非常有用,这就是React.cloneElement,我们来修改一下Layout

代码语言:css
复制
function Layout({ header, children }) {
  return (
    <div className='container'>
      <div className='header'>
        {React.cloneElement(header, { color: 'green' })}
      </div>
      <div classNmae='content'>{children}</div>
    </div>
  )
}

通过这样,我们真正渲染出来的Header他的props.color就永远都是green。那么这个 API 是啥意思呢?

顾名思义,他是用来克隆一个ReactElement,他接收三个参数,第一个是目标element,第二个是props,第三个是children。可见他跟createElement非常像,唯一的区别是第一个参数从组件变成来节点。参考 React面试题详细解答

他做的事情其实就是拷贝目标element,并把后面两个参数覆盖原elementprops,以此创建一个新的ReactElement


那么到此,我们的优化过程也差不多来,当然 demo 显然是非常简单的代码,现实中的问题往往要复杂很多,比如接收的如果不是一个ReactElement而是数组,字符串该如何处理。那么这些问题在这里就不再继续深入来,留给各位小伙伴自己去思考吧,毕竟万变不离其宗,知道了核心思路之后,其他问题也就可以迎刃而解来。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 版本一
  • 第二版
  • 第三版
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档