Bereaksi: slot seperti anak teman ibu

Saat membuat komponen, tugas menyesuaikan konten komponen sangat sering muncul. Misalnya, kita memiliki komponen DatePicker dan kita perlu menampilkan tombol Terapkan yang berbeda di berbagai bagian aplikasi web.

Untuk mengatasi masalah tersebut, setiap teknologi populer saat ini menggunakan konsep "slot". Angular memiliki ngContent , Vue , Svelte dan WebComponents memiliki slot. Dan hanya library React yang populer yang tidak memiliki konsep slot yang lengkap saat ini.

Ada beberapa pendekatan untuk menyelesaikan masalah ini di React:

  1. Sebuah komponen dapat merender semua turunannya secara keseluruhan, atau "memanjat" ke dalamnya melalui React.Children API dan memanipulasi anak-anak secara tepat

  2. Sebuah komponen bisa mendeklarasikan apa yang disebut renderProps , dan merender konten yang dikembalikan darinya di tempat yang tepat:

    <MyComponent renderFooter={data => (<h1>Bye, ${data.name}</h1>)}/>

Pendekatan renderProps umumnya terkenal dan tidak memiliki kelemahan mendasar. Kecuali jika tidak terlalu nyaman untuk menggunakannya, dibandingkan dengan slot penuh. NPM memiliki beberapa pustaka, seperti react-view-slot , tetapi saya rasa mereka tidak cukup nyaman dan, yang paling penting, cukup menyelesaikan masalah.

Saya memutuskan untuk mencoba memperbaiki kesalahan fatal ini , dan sekarang saya akan memberi tahu Anda caranya.

Saya melihat tujuannya - saya tidak melihat implementasinya

Sebelum memprogram apa pun, ada baiknya Anda mengetahui API mana yang ingin Anda dapatkan. Beginilah sketsa saya tentang hasil yang diinginkan:

const Component = props => {
  Component.NameSlot = useSlot(props.children);

  return (
    <div>
      <h1>
        <Component.NameSlot.Receiver>
          Default value
        </Component.NameSlot.Receiver>
      </h1>

      Hello {props.children}
    </div>
  );
};

function App() {
  return (
    <div>
      Hello!
      <Component>
        Foo
        <Component.NameSlot>
          Bobobo
        </Component.NameSlot>
      </Component>
    </div>
  );
}

Idenya adalah untuk membuat slot yang diperlukan dan menyimpan komponen fungsi dalam properti statis, dan kemudian menggunakannya dengan tepat di sisi pengirim dan penerima.

, . – , , , -, . , , API, :

import {createSlot} from 'react-slotify';

export const MySlot = createSlot();

export const Component = ({children}) => {
  return (
    <div>
      This component contains slot:
      
      <MySlot.Renderer childs={children}>
        This is default slot content
      </MySlot.Renderer>
      
      <div>It also renders children: {children}</div>
    </div>
  );
};
import {Component, MySlot} from './component';

const App = () => {
  return (
    <div>
      <Component>
        <MySlot>Slotted content</MySlot>
        Other content
      </Component>
    </div>
  );
};

, API, , –  MySlot, {children}, , MySlot.Renderer. , JS-, :

export function createSlot() {
  const Slot = ({ children, showChildren }) => {
    return showChildren ? children : null;
  }

  const Renderer = ({ childs, children }) => {
    const slotted = React.Children.toArray(childs).find(child => {
      return React.isValidElement(child) && child.type === Slot;
    });

    if (!slotted || !React.isValidElement(slotted)) {
      return children;
    }
    return React.cloneElement(slotted, { showChildren: true });
  };

  Slot.Renderer = Renderer;

  return Slot;
}

-, 20 . , React- , . . –  Slot. , :

export function createSlot() {
  const Slot = ({ children, showChildren }) => {
    return showChildren ? children : null;
  }
  return Slot;
}

, Slot –  , showChildren={true}. , , Slot .

– Renderer. –  -, Slot-, , showChildren={true}:

  const Renderer = ({ childs, children }) => {
    const slotted = React.Children.toArray(childs).find(child => {
      return React.isValidElement(child) && child.type === Slot;
    });

    if (!slotted || !React.isValidElement(slotted)) {
      return children;
    }
    return React.cloneElement(slotted, { showChildren: true });
  };

, Renderer , , Slot . .

–  Renderer Slot, : <MySlot.Renderer/>.

Jadi, dalam 20 baris kode, kami telah menerapkan konsep yang sangat disukai oleh banyak pengembang di teknologi lain, dan yang tidak dimiliki React.

Saya menerbitkan implementasi yang sudah selesai sebagai library react-slotify di GitHub dan sebagai paket di NPM . Sudah ada di TypeScript dan dengan dukungan untuk parameterisasi slot . Saya akan senang menerima kritik yang membangun.




All Articles