I recently developed a multi-level dropdown menu, and I had to think about making this component as reusable as possible.
My best take on this was to pass down to the component an object, defining the actual structure of the dropdown menu.
Something like this:
const content = [
{
value: "Item 1",
onClick: () => console.log("Clicked 1"),
},
{
value: "Item 2",
children: [
{
value: "Item 2.1",
children: [
{
value: "Item 2.1.1",
onClick: () => mockFN,
},
{
value: "Item 2.1.2",
onClick: () => mockFN,
},
{
value: "Item 2.1.3",
},
{
value: "Item 2.1.4",
},
{
value: "Item 2.1.5",
onClick: () => mockFN,
},
],
},
],
},
];
return (
<Dropdown content={content} trigger={"Test"} />
);
Of course, this being in TypeScript, everything is handled with a type:
type DropdownItem = {
value: string;
color?: string;
textColor?: string;
children?: DropdownItem[];
onClick?: () => void;
};
What I noticed browsing online though, is that some components like this one are actually handled in a different way, for example react-multilevel-dropdown actually exposes not only a Dropdown
component, but also some sub-components like Dropdown.Item
and Dropdown.Submenu
<Dropdown
title='Dropdown title'
>
<Dropdown.Item
onClick={() => doSomething()}
>
Item 1
</Dropdown.Item>
<Dropdown.Item>
Item 2
<Dropdown.Submenu>
<Dropdown.Item>
Subitem 1
</Dropdown.Item>
</Dropdown.Submenu>
</Dropdown.Item>
</Dropdown>
Now here come the questions:
Is there a name to identify these 2 ways of structuring a component?
Should I always prefer one over the other? Or does it depend on what's to be done? One thing that comes to my mind is, if the component's content needs to be generated by a backend payload, the "object" one would be significantly easier to deal with (that's why I went with that route in the end)
How are the subcomponents coded like? I'm pretty sure I can't name a component with a period in it. So is it something like Dropdown
is a class component, and Dropdown.Item
is a property of that class?
Thanks for the help!
Ok I went a bit more in depth on the subject, and basically found that:
import React from 'react';
type DropdownProps = { title: string; children: React.ReactNode };
type ItemProps = { onClick?: () => void; children: React.ReactNode };
const Dropdown: React.FC<DropdownProps> & { Item: React.FC<ItemProps> } = (props) => {
return <div>{props.children}</div>;
};
Dropdown.Item = (props) => {
return <div onClick={props.onClick}>{props.children}</div>;
};
export default Dropdown;