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 (1)

Collapse
 
hendrikras profile image
Hendrik Ras

This is an interesting approach, Ill give it a try.
Strange that React still doesn't support slots while Vue has it by default.

..

Just a heads up that you can add highlighting to the code blocks if you'd like. Just change:

code block with no colors example

... to specify the language:

code block with colors example

More details in our editor guide!