React composition issues

I’m having a few issue with React composition. For isntance:

const FormActions: React.FC<any> = () => {

    return (
        <Fab position="right-bottom" slot="fixed" color="pink">
            <Icon f7="question"></Icon>
            <FabButtons position="top">
                <FabButton label="Show Errors">
                    <Icon f7="rays"></Icon>
                </FabButton>
            </FabButtons>
        </Fab>
    );

}

<Page name="multiStepDomainFormPage">
       <FormActions />
</Page>

The FAB is placed in page content area.

But if I change it to this:

<Page name="multiStepDomainFormPage">
       <div slot="fixed"><FormActions /></div>
</Page>

The the FAB is inserted inside a div which is a direct child of “page” right before “page-content” which is correct.

This happens to other elements as well when using composition. If I extract ListItem to a composable component then I must manually wrap it with ul element.

Is there a explanation for this, and what are the best practices for React composition.

When you use custom component F7 can’t detect what is inside. It is by design, just use slots:

<Page name="multiStepDomainFormPage">
       <FormActions slot="fixed" />
</Page>

Thanks, that works.

Some components do not have slots.

const InputController: React.FC<any> = () => {
    return (
        <ListInput
            ...  />
    );
}

<List>
       <InputController  />
</List>

This render without ul element. Currently I am doing this to compensate:

<List>
       <ul>
                 <InputController  />
       </ul>
</List>

Would you do it similarly or is there another way?

List has slots https://framework7.io/react/list-view.html#list-slots

I’ve just come across this behaviour. The problem with this is that you can’t then control the order of the items in a list if you are mixing custom components with standard ListItem components.
This really breaks composability.

I just worked out how to get around this somewhat, in case anyone else finds this:
I changed my custom component to be an Input component, then wrapped that in a ListInput component with input={false} in my main List component.

I’ve found that this works for me:

export const SlotItem: React.FC<{ slot: string, renderFn: () => any }> = (props) => props.renderFn();

Then just use slot ‘list’.

I had the need to render a component that wrapped a ListItem and to pass the slot items as components themselves.

Passing it as children did not work as children is an array. So I ended up with the following pattern that did the job:

interface IProps extends ListItemProps { 
    slotRenderers?: { [slot: string]: () => JSX.Element } // MUST be object literal
}
export const SomeListItemWrapper: FC<IProps> = (props) => {

    return <ListItem
        {...props}
    >
    {
         props.slotRenderers?.['subtitle'] && 
            <span slot="subtitle">{props.slotRenderers?.['subtitle']()}</span>
    }
    // repeat for all the slots on ListItem
    </ListItem>;

}

Note that I passed slotRenderers as an object literal and not an array. Passing it as an array does not work.