IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    react 实现插槽slot功能

    时倾发表于 2024-10-10 14:08:42
    love 0

    背景

    在开发一个需求时,需要对原来的 form 表单组件代码复用并进行拓展。
    场景A 使用原来的 form 表单组件。
    场景B 在原来的表单组件基础上,新增一些表单项,新增表单项位置动态插入在原来的表单组件中,位置随意。

    需求

    复用表单组件,同时支持新增表单项。

    解决方案

    在 React 中,组件扩展和定制的能力,可以通过 props.children 和 render props 来实现。

    以上两种方式的缺点是:如果插入位置比较分散,需要定义children对象或多个 props,代码繁琐,不易维护。调研下来,目前貌似没其他好的方法... 欢迎补充

    props.children

    props.children 直接将内容作为一个HTML内嵌结构编写,将组件参数与内嵌结构分开写。
    children 可以是一个字符串, 数组,对象等类型。可以使用 React.Children 的方法来判断props.children 类型并处理。

    function Father() {
        return (
            <div>
                我是父组件Father
                <Form1>
                  <div>我是子组件Form1的children</div>
                </Form1>
                <Form2>
                    {{
                        title: (<div>我是子组件Form2的title</div>),
                        content: (<div>我是子组件Form2的content</div>)
                    }}
                </Form2>
            </div>
        )
    }
    
    function Form1(props) {
        return (
            <div>
                我是子组件Form1
                {props.children}
            </div>
        )
    }
    
    function Form2(props) {
        return (
            <div>
                我是子组件Form2
                {props.children.title}
                {props.children.content}
            </div>
        )
    }

    render props

    通过 props 参数传入 JSX 元素的方法渲染,告知组件需要渲染什么内容的函数 prop。可以定义多个 props 参数,不同位置渲染不同的 props。

    function Father() {
        return (
            <div>
                我是父组件Father
                <Form1
                  children={<div>我是子组件Form1的children</div>}
                />
                <Form2
                  title={<div>我是子组件Form2的title</div>}
                  content={<div>我是子组件Form2的content</div>}
                />
            </div>
        )
    }
    
    function Form1(props) {
        return (
            <div>
                我是子组件Form1
                {props.children}
            </div>
        )
    }
    
    function Form2(props) {
        return (
            <div>
                我是子组件Form2
                {props.title}
                {props.content}
            </div>
        )
    }

    dataset

    React 没有专门的插槽,根据 children/props 的特性,加上只读属性 dataset 实现一个类似的插槽功能。

    非必要不使用,代码会更加繁琐。
    如果有条件判断是否展示,可以灵活设置 dataset 值使用。
    function Father() {
      return (
        <div>
          我是父组件Father
          <Form1
            type='text1'
            title={<div>我是子组件Form的title</div>}
            bottom={<div>我是子组件Form的bottom</div>}
          >
            <div data-type='text1'>
              <label>性别:</label>
              <input type="text" name="gender" />
            </div>
            <div data-type='text1,text2'>
              <label>身高:</label>
              <input type="text" name="height" />
            </div>
            <div data-type='text2,text3'>
              <label>体重:</label>
              <input type="text" name="weight" />
            </div>
          </Form1>
        </div>
      )
    }
    
    function Form1({
      title,
      bottom,
      type,
      children,
    }) {
      const renderChild = useMemo(() => {
        return children
          .filter(itm => itm.props['data-type']?.split(',')?.includes(type))
          .map(itm => itm.props.children)
      }, [type, children])
    
      return (
        <div>
            我是子组件Form1
            {title}
            <div>
              <label>姓名:</label>
              <input type="text" name="name" />
            </div>
            {renderChild}
            <div>
              <label>年龄:</label>
              <input type="number" name="age" />
            </div>
            {bottom}
        </div>
      )
    }


沪ICP备19023445号-2号
友情链接