React.cloneElement的使用详解

 更新时间:2021年04月06日 10:33:24   作者:fullstackbb  
这篇文章主要介绍了React.cloneElement的使用详解,帮助大家更好的理解和学习使用React框架,感兴趣的朋友可以了解下

因为要接手维护一些项目,团队的技术栈最近从 vue 转向 react ,作为一个 react 新手,加上一向喜欢通过源码来学习新的东西,就选择了通过阅读 antd 这个大名鼎鼎的项目源码来学习一些 react 的用法。

在阅读源码的过程中,发现好些组件都使用了 React.cloneElement 这个 api ,虽然通过名字可以猜测它做了什么,但是并不知道具体的作用;然后去看官方文档,文档很清晰地描述了它的作用,却没有告诉我们什么场景下需要使用它。于是我根据文档的描述,结合源码的使用,面向 google 和 stackoverflow,总结出来一些使用场景。

cloneElement 的作用

React.cloneElement(
 element,
 [props],
 [...children]
)

首先看一下官方文档对这个 API 的描述:

Clone and return a new React element using element as the starting point. The resulting element will have the original element's props with the new props merged in shallowly. New children will replace existing children. key and ref from the original element will be preserved.

总结下来就是:

  1. 克隆原来的元素,返回一个新的 React 元素;
  2. 保留原始元素的 props,同时可以添加新的 props,两者进行浅合并;
  3. key 和 ref 会被保留,因为它们本身也是 props ,所以也可以修改;
  4. 根据 react 的源码,我们可以从第三个参数开始定义任意多的子元素,如果定义了新的 children ,会替换原来的 children ;

使用场景

根据上面的定义分解,我们可以在不同的场景下根据需要来使用这个 api 。

添加新的 props

当我们创建一个通用组件时,根据内部的逻辑,想要给每个子元素添加不同的类名,这个时候我们可以修改它的 className

假设我们有一个 Timeline 组件,允许我们根据需要定义多个 TimelineItem ,在内部我们想要给最后一个TimelineItem 添加一个 timeline-item-last 类来渲染特殊的效果,这个时候我们可以这样做:

const MyTimeline = () => {
 return (
  <Timeline>
   <TimelineItem>2020-06-01</TimelineItem>
   <TimelineItem>2020-06-08</TimelineItem>
   <TimelineItem>2020-07-05</TimelineItem>
  </Timeline>
 )
}

// 在 Timeline 内部,逻辑可能是这样的
import class from 'classnames';
const Timeline = props => {
 // ...
 // ...
 const itemCount = React.children.count(props.children);
 const items = React.children.map(props.children, (item, index) => {
  return React.cloneElement(item, {
   className: class([
    item.props.className,
    'timeline-item',
    index === count - 1 ? 'timeline-item-last' : ''
   ])
  })
 }
 return <div className={'timeline'}>{ items }</div>
}

除了添加 className ,还可以动态给子组件添加更多的 props 信息,react-router Switch 会给匹配的子组件添加 locationcomputedMatch 信息:

class Switch extends React.Component {
 render() {
  return (
   <RouterContext.Consumer>
    {context => {
     invariant(context, "You should not use <Switch> outside a <Router>");

     const location = this.props.location || context.location;

     let element, match;

     // We use React.Children.forEach instead of React.Children.toArray().find()
     // here because toArray adds keys to all child elements and we do not want
     // to trigger an unmount/remount for two <Route>s that render the same
     // component at different URLs.
     React.Children.forEach(this.props.children, child => {
      if (match == null && React.isValidElement(child)) {
       element = child;

       const path = child.props.path || child.props.from;

       match = path
        ? matchPath(location.pathname, { ...child.props, path })
        : context.match;
      }
     });

     return match
      ? React.cloneElement(element, { location, computedMatch: match })
      : null;
    }}
   </RouterContext.Consumer>
  );
 }
}

修改 props 的事件

假设我们有一个 Tab 组件,它下面包含多个 TabPane 子组件,我们想要点击每个 TabPane 子组件的同时触发 Tab 的 onClick 事件,用户自己本身可能给每个 TabPane 定义了独立的 onClick 事件,这时候我们就要修改子组件 onClick 事件:

const Tab = props => {
 const { onClick } = props;
 const tabPanes = React.children.map(props.children, (tabPane, index) => {
  const paneClick = () => {
   onClick && onClick(index);
   tabPane.props?.onClick();
  }
  return React.cloneElement(tabPane, {
    onClick: paneClick,
  })
 })
 return <div>{ tabPanes }</div>
}

定制样式

创建一个叫 FollowMouse 组件时,我们允许用户定义内容组件 Content ,当鼠标移动时,根据内容的大小,自动计算 Content 的位置避免溢出屏幕,这个时候我们就可以使用 cloneElement 来动态修改它的样式。

// 简单起见,这里省略鼠标事件。
const FollowMouse = props => {
 const { Content } = props;
 const customContent = React.isValidElement ? Content : <span>{ Content }</span>
 const getOffset = () => {
  return {
   position: 'absolute',
   top: ...,
   left: ...,
  }
 }
 const renderContent = React.cloneElement(custonContent, {
  style: {
   ...getOffset()
  }
 })
 return <div>{ renderContent() }</div>
}

添加 key

当我们创建一个元素列表时,可以通过 cloneElement 给每个节点添加一个 key 。

const ComponentButton = props => {
 const { addonAfter, children } = props;
 const button = <button key='button'>{ children }</button>
 const list = [button, addonAfter ? React.cloneElement(addonAfter, { key: 'button-addon' } : null)
 return <div>{ list } <div>
}

总结

在开发复杂组件中,经常会根据需要给子组件添加不同的功能或者显示效果,react 元素本身是不可变的 (immutable) 对象, props.children 事实上并不是 children 本身,它只是 children 的描述符 (descriptor) ,我们不能修改任何它的任何属性,只能读到其中的内容,因此 React.cloneElement 允许我们拷贝它的元素,并且修改或者添加新的 props 从而达到我们的目的。

当然,得益于 react 强大的组合模式,这并不仅仅局限于 props.children ,不管是 props.left 还是 props.right 或者任何其它的 props 传进来的内容,只要是合法的 react 元素,我们都可以使用这个 React.cloneElement 对其进行操作。

以上就是React.cloneElement的使用详解的详细内容,更多关于React.cloneElement的使用的资料请关注脚本之家其它相关文章!

相关文章

  • react-diagram 序列化Json解读案例分析

    react-diagram 序列化Json解读案例分析

    今天带来大家学习react-diagram 序列化Json解读的相关知识,本文通过多种案例给大家分析序列化知识,通过图文并茂的形式给大家介绍的非常详细,感兴趣的朋友一起看看吧
    2021-05-05
  • React插槽使用方法

    React插槽使用方法

    本文主要介绍了React插槽使用方法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-12-12
  • Unity RectTransform详解

    Unity RectTransform详解

    unity中的ui元素是有严格的父子关系的,子物体的位置是根据父物体的变化而变化的,而子物体和父物体联系的桥梁就是Anchor,本文重点介绍Unity RectTransform的相关知识,感兴趣的朋友一起看看吧
    2024-01-01
  • 关于React中setState同步或异步问题的理解

    关于React中setState同步或异步问题的理解

    相信很多小伙伴们都一直在疑惑,setState 到底是同步还是异步。本文就详细的介绍一下React中setState同步或异步问题,感兴趣的可以了解一下
    2021-11-11
  • React生命周期与父子组件间通信知识点详细讲解

    React生命周期与父子组件间通信知识点详细讲解

    生命周期函数指在某一时刻组件会自动调用并执行的函数。React每个类组件都包含生命周期方法,以便于在运行过程中特定的阶段执行这些方法
    2022-11-11
  • react中Hooks的理解和用法小结

    react中Hooks的理解和用法小结

    Hook是 React 16.8 的新增特性,它可以让你在不编写class的情况下使用state以及其他的React特性,这篇文章主要介绍了react中Hooks的理解和用法,需要的朋友可以参考下
    2023-05-05
  • react使用节流函数防止重复点击问题

    react使用节流函数防止重复点击问题

    这篇文章主要介绍了react使用节流函数防止重复点击问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • 手把手带你用React撸一个日程组件

    手把手带你用React撸一个日程组件

    这篇文章主要给大家介绍了关于利用React撸一个日程组件的相关资料,包括日常组件的实现思路、使用的技术、以及遇到的技术难点,并给提供了详细的实例代码,需要的朋友可以参考下
    2021-07-07
  • react在安卓中输入框被手机键盘遮挡问题的解决方法

    react在安卓中输入框被手机键盘遮挡问题的解决方法

    这篇文章主要给大家介绍了关于react在安卓中输入框被手机键盘遮挡问题的解决方法,文中通过图文以及示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧
    2018-09-09
  • 浅谈箭头函数写法在ReactJs中的使用

    浅谈箭头函数写法在ReactJs中的使用

    这篇文章主要介绍了浅谈箭头函数写法在ReactJs中的使用,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08

最新评论