DEV Community

cieldon32
cieldon32

Posted on

How to use slots in react elegantly

We all know, we can use props to implement component slots. This is a good way, but the props of the components will become more and more.
Also, you can filter the children by element's type, but it isn't a good idea.
And, you can use HOC to wrap the Component, and use Proxy to reduce the children. you can use the subComponent, like:

<YouComponent.SubComponentA>a</YouComponent.SubComponentA>
Enter fullscreen mode Exit fullscreen mode

And then, your Component's props will get:

slot: {
   SubComponentA: {children, props}
}
Enter fullscreen mode Exit fullscreen mode

This is a good idea. But There is a better way.
We focus on children, children will be a array, or string, or a ReactElement, or undefined.
The ReactElement has type, you can check the child with the type's name.
You will use your Component like this:

<YouComponent>
    <Slot name="SubComponentA">a</Slot>
    b
</YouComponent>
Enter fullscreen mode Exit fullscreen mode

<Slot /> is a component that it render null. And it has a generator function, like this:

import {ReactElement} from 'react'
import {SlotProps} from './slot.interface';

function Slot(_: SlotProps): ReactElement {
  return null as unknown as ReactElement;
}

Slot.getSlot = function* getCollectionNode(props: SlotProps): any {
  yield props.children;
};

let _Slot = Slot as (props: SlotProps) => JSX.Element;
export default _Slot;
Enter fullscreen mode Exit fullscreen mode

And then, In your component's code, you can use a hook to reduce the children. The hook like this:

import React from 'react';

export function useSlot(children: any) {
  const result: Record<string, any> = {
    children: []
  };
  React.Children.map(children, (child: React.ReactElement<any, React.JSXElementConstructor<any>>) => {
    if(React.isValidElement(child)){
      const type = child.type as any;
      const props = child.props;
      if(type.name === 'Slot') {
        const slot = type.getSlot(child.props);
        result[props.name] = slot.next().value;
      } else {
        result['children'].push(child);
      }
    } else {
      result['children'].push(child);
    }
  })
  return result;
}
Enter fullscreen mode Exit fullscreen mode

the result of the useSlot(children), will be :

{
   SubComponentA: 'a',
   children: ['b']
}
Enter fullscreen mode Exit fullscreen mode

That is my way to use slot in React. Do you have another way? Please tell me.

Top comments (0)