React中如何使用类vue的插槽模式,以及React.cloneElement
React中实现插槽模式
👉 sandbox 👈
概念
插槽:从父组件中传递一个对像给子组件,这个对象中的内容可以是组件,也可以是一段文本内容,还可以是一个事件方法
插槽分为匿名和具名两种模式
- 匿名插槽:直接通过props.children传递
 
- 具名插槽:在props作为参数传递(函数式组件本质上是个函数,可以做参数)
 
注意:插槽的使用逻辑是子组件是封装好的工具,某些部分是固定的,而某些部分是按场景区分的,我们提前知道这些部分的渲染位置,而渲染内容不一致。
例如:
子组件是一个抽屉框Drawer组件,它的Header位置可能是一个控制导航的NavBar组件,也可能是一个搜索框Input组件。
这时候在上层调用Drawer时,就需要由父组件来定义Header具体是什么组件,并且将什么数据传入Header。
同时子组件Drawer中只处理共性的抽象的逻辑:渲染Header组件,并将收到的参数传入Header组件
代码
👉 sandbox 👈
渲染结果:
chilren Body
slot footer
具名插槽:SlotHeader,SlotFooter
匿名插槽:children的部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
   |  import Child from "./Child"; export default function Parent() {   const SlotHeader = (props) => <h1>{props.title}</h1>;   const slotHeaderProps = {     title: "slot header"   };   const SlotFooter = (props) => <p>{props.content}</p>;   const slotFooterProps = {     content: "slot footer"   };   return (     <Child       SlotHeader={SlotHeader}       slotHeaderProps={slotHeaderProps}       SlotFooter={SlotFooter}       slotFooterProps={slotFooterProps}     >       chilren Body     </Child>   ); }
 
  export default function Child(props) {   const { SlotHeader, slotHeaderProps, SlotFooter, slotFooterProps } = props;   return (     <div>       {SlotHeader ? <SlotHeader {...slotHeaderProps}></SlotHeader> : null}       <div>{props.children}</div>       {SlotFooter ? <SlotFooter {...slotFooterProps}></SlotFooter> : null}     </div>   ); }
 
  | 
 
延伸:React.cloneElement
使用React.cloneElement方法可以高效地对插槽组件注入内容
上面我们使用插槽的过程是:在上层组件决定要使用的子组件、子组件的输入参数和children
而React.cloneElement方法就提供了一种快捷地组合以上三者的方式,并返回注入完各种参数的子组件实例
参考:https://www.jianshu.com/p/2ccf0cd14388
api
1 2 3 4 5
   | React.cloneElement(   element,   [props],   [...children] )
   | 
 
element:必须是一个React组件或者原生DOM实例,例如<div />,<List />
props:传入element的props对象
children:element的children
代码
👉 sandbox 👈
渲染结果:
user list
下面的例子中想要渲染一个列表,List和ListItem可以看作是事先定义好的工具组件,而UserList是一个业务组件,它按需地对List和ListItem进行组合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
   |  import React from "react"; import List from "./List"; import ListItem from "./ListItem";
  export default function UserList(props) {   const userList = ["tom", "jerry", "jake"];   const Title = () => <h2>user list</h2>;   return React.cloneElement(     <List />,      { Title },           userList.map((item, idx) => <ListItem>{`${idx}: ${item}`}</ListItem>)   ); }
 
  export default function List(props) {   const { Title } = props;   return (     <div>       {Title ? <Title /> : null}       <ul>{props.children}</ul>     </div>   ); }
 
  export default function ListItem(props) {   return <li>{props.children}</li>; }
 
 
  |