React系列:React.Children && React.cloneElement

277 阅读2分钟

1. React.Children

用 React.Children,你可以处理 props.children,主要方法如下:

React.Children.map

React.Children.map(children, function[(thisArg)])

在 children 里的每个直接子节点上调用一个函数。如果 children 是一个数组,它将被遍历并为数组中的每个子节点调用该函数。如果子节点为 null 或是 undefined,则此方法将返回 null 或是 undefined,而不会返回数组。

注意

如果 children 是一个 Fragment 对象,它将被视为单一子节点的情况处理,而不会被遍历。

React.cloneElement()

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

以 element 元素为样板克隆并返回新的 React 元素。返回元素的 props 是将新的 props 与原始元素的 props 浅层合并后的结果。新的子元素将取代现有的子元素,而来自原始元素的 key 和 ref 将被保留。

React.cloneElement() 几乎等同于:

<element.type {...element.props} {...props}>{children}</element.type>

我为什么说是“几乎”呢?有什么差别呢?

差别在于,React.cloneElement()保留了组件的 ref。相同的 ref 将添加到克隆后的新元素中。

例子:

栅格Card对齐

栅格布局下,如果每个元素是个Card,内容是后端给的,他们的高度不统一,可能出现参差不齐的情况:

import React from 'react';
import { Row, Col } from 'antd';

const Child = (props) => {
   // 解构props
   const {
       length,
   } = props;
   const name = new Array(length).fill('R').join(' ');
   return (
       <div style={{ background: 'red'}}>
           {name}
       </div>
   );
 }
 
 const data = [20, 30, 40, 50]
 
 const App = () => {
     return (
         <div style={{width: '800px',height: '400px',backgroundColor: '#ccc'}}>
             <Row gutter={24}>
                 {data.map((item, index) => (
                     <Col key={index} span={6}>
                         <Child length={item}/>
                     </Col>
                 ))}
             </Row>
         </div>
     )
 export default App;

效果:

8cbff6dfe2a5d9d86bc869a4227717d.png

解决方案

当然,你可以利用flex,设置Row是flex,并设置align-items: stretch;,并为每个<App />添加样式height: 100%

import React from 'react';
import { Row, Col } from 'antd';

const Child = (props) => {
    // 解构props
    const {
        length,
        style
    } = props;
    const name = new Array(length).fill('A').join(' ');
    return (
        <div style={{ background: 'red',...style}}>
            {name}
        </div>
    );
};

const data = [20, 30, 40, 50
const App = () => {
    return (
        <div style={{width: '800px',height: '400px',backgroundColor: '#ccc'}}>
            <Row gutter={24} style={{ alignItems: 'stretch'}}>
                {data.map((item, index) => (
                    <Col key={index} span={6}>
                        <Child style={{height: '100%'}} length={item}/>
                    </Col>
                ))}
            </Row>
        </div>
    )

export default App;

效果:

cb1f5832e85ba0c9431e395f811849a.png

最方便的是封装一下Row和Col。并且在Col中直接给children的所有元素赋style值 height: 100%。大大减少了开发者心智负担。(开发组件库时很有用,尤其是基于antd等封装自己的业务组件时)。

封装:

import React from 'react';
import { Row, Col } from 'antd';

const JRow = (props) => {
  return (
    <Row
      {...props}
      style={{ alignItems: 'stretch', ...props.style }}
    />
  );
};

const JCol = (props) => {
  const { children, ...otherProps } = props;
  return (
    <Col
      {...otherProps}
    >
      {React.Children.map(children, (child) => {
        return React.cloneElement(
          child,
          { style: { ...child.props.style, height: '100%' }}
        );
      })}
    </Col>
  );
};

export {JRow,JCol};

引用:

import React from 'react';
// 引入JRow,JCol
import { JRow, JCol } from '../../components/Gird';

const Child = (props) => {
    // 解构props
    const {
        length,
        style
    } = props;
    const name = new Array(length).fill('A').join(' ');
    return (
        <div style={{ background: 'red',...style}}>
            {name}
        </div>
    );
};

const data = [20, 30, 40, 50];

const App = () => {
    return (
        <div style={{width: '800px',height: '400px',backgroundColor: '#ccc'}}>
            <JRow gutter={24}>
                {data.map((item, index) => (
                    <JCol key={index} span={6}>
                        <Child length={item}/>
                    </JCol>
                ))}
            </JRow>
        </div>
    )
}
export default App;

文章引用地址:[React] 你不了解但有用的API: React.Children 与 React.cloneElement - 掘金 (juejin.cn)