React 中 children 实现插槽的功能

7,363 阅读2分钟

React 写组件,通常是组件与组件之间相互引用,组合成一个树状的结构,进行渲染。

我们自己封装组件的时候,为了统一组件顶层数据来源 和 在组件内部常常需要自定义一些东西,做类似于插槽的功能。直接将 jsx 内容插入到组件内部,而不是通过组件引入的方式,引入组件内部。

<App>
  <About name='name'></About>
  <About name='fisrtName'></About>
</App>

children 一般用的比较少,但是在组件封装时候,用处就比较大了。About 组件集合会通过 App 组件的 props.children 中访问到。

children

  • 对应关系

props.children 是 react 元素集合,可直接渲染在 render 函数中。props 与父组件的插槽位置元素相对应。如果有过 Vue 经验,对应关系很好理解。

  • 本质

对象,如果看过 React 源码,就知道 React 其实就是一个个对象,来描述这个组件应该如何渲染。

  • 访问

通过 props 直接访问

  • 改造 children

api:

- React.Children API
- React.cloneElement

上面提到 children 本质是一个模板 React 元素对象,那么我们应该如何这些模板就可通过 props 访问来改造重新组装来渲染新的 React 组件, 达到了使用

const newEl = React.cloneElement(child, newchildProps)

一个简单的例子

使用 Create-React-App 创建一个例子

// main.js
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";

// components
import About from "./components/About";

ReactDOM.render(
  <React.StrictMode>
    <App>
      <About name='name'></About>
      <About name='fisrtName'></About>
    </App>
  </React.StrictMode>,
  document.getElementById("root")
);

App 组件是主要渲染的组件,它的插槽位置有两个 React 元素,他们在 App 组件内部使用 props.children 可访到该集合。

<About name='name'></About>
<About name='fisrtName'></About>

About 组件的实现很简单, 注意:只是在插槽或者children位置,传入 name 的 props, 并没有传入 haxi 的 props,但是使用了 haxi, 好像这个haxi 成了魔术数字, 这也是接下来需要改造的 App 组件的地方:

import React from 'react';

export default function About(props){
  return <div>{props.name}-{props.haxi}</div>
}

在 App 组件内部实现 props 的内容改造,然后克隆一个新的元素渲染,其实我们这里只是将 props.children 当成了模板,在渲染的时候是重新改造了新元素,下面我们实现这个简单的改造过程:

import React from "react";
import logo from "./logo.svg";
import "./App.css";

function App(props) {
	// 获取 props
  const { children } = props;	
  // 遍历生成新的 El 集合
  const child = React.Children.map(children, (item, index) => {
    const childProps = {
      ...item.props,
      // 使用 index 赋值 haxi 属性
      haxi: index
    };
    // 使用 元素和新的 props 克隆一个新的 react 元素
    return React.cloneElement(item, childProps);
  });

  return (
    <div className='App'>
      <header className='App-header'>
          // 直接渲染新的 child
          {child}
        </a>
      </header>
    </div>
  );
}

export default App;

优点

  1. 组件全部放在顶层,数据来源直接在顶层定义,方便快捷
  2. 结构更加明确,使用简单,没有更多层次的组件引用
  3. 充分的认识 React 本质和 React.Children + props.children API