反応:お母さんの友達の息子のようなスロット

コンポーネントを作成する場合、コンポーネントのコンテンツをカスタマイズするタスクが頻繁に発生します。たとえば、DatePickerコンポーネントがあり、Webアプリケーションのさまざまな部分にさまざまな[適用]ボタンを表示する必要があります。

このような問題を解決するために、今日の一般的なテクノロジーはすべて「スロット」の概念を使用しています。アンギュラはありngContentVueのスヴェルトWebComponentsをスロットをしています。そして、人気のあるReactライブラリだけが、今日のスロットの本格的な概念を持っていません。

Reactでこの問題を解決するには、いくつかのアプローチがあります。

  1. コンポーネントは、すべての子を完全にレンダリングするか、React.Children APIを介して子に「登り」、点ごとに子を操作することができます。

  2. コンポーネントはいわゆるrenderPropsを宣言し、コンポーネントから返されたコンテンツを適切な場所にレンダリングできます。

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

renderPropsアプローチは一般的によく知られており、根本的な欠陥はありません。本格的なスロットと比較して、使い勝手が悪い場合を除きます。NPMにはreact-view-slotなどのいくつかのライブラリがありますが、それらは十分に便利ではなく、最も重要なこととして、単に問題を解決するだけだと思います。

私はこの致命的な欠陥を修正しようと決心しました、そして今私はあなたにその方法を教えます。

目標はわかります-実装はわかりません

何かをプログラミングする前に、取得したいAPIを知っておくと便利です。これは、望ましい結果の私のスケッチがどのように見えたかです:

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>
  );
}

アイデアは、必要なスロットを作成し、関数コンポーネントを静的プロパティに格納してから、送信側と受信側で適切に使用することでした。

, . – , , , -, . , , 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/>.

したがって、20行のコードで、他のテクノロジーの多くの開発者が本当に気に入っている、Reactに欠けているコンセプトを実装しました。

私はとして完成し、実装を発表した反応-slotifyライブラリのGitHubとのパッケージとしてNPMすでにTypeScriptにあり、スロットのパラメータ化をサポートしています建設的な批判をいただければ幸いです。




All Articles