react 實現插槽slot功能

啦啦乌托邦發表於2024-12-01

背景
在開發一個需求時,需要對原來的 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>
  )
}

相關文章