S
SolidJS15mo ago
Mathieu

Say I have a `Card` component, How can I have a `Card.Header` component?

Say I have a Card component, How can I have a Card.Header component?
32 Replies
REEEEE
REEEEE15mo ago
Card.Header = HeaderComponent
Mathieu
Mathieu15mo ago
const Card: ParentComponent<Props> = (props) => {
return //...
};
Card.Header = Header;
export default Card;
const Card: ParentComponent<Props> = (props) => {
return //...
};
Card.Header = Header;
export default Card;
TS error:
Property 'Header' does not exist on type 'ParentComponent<Props>'
it works though, thanks!! just gotta fix that TS error now
jesseb34r
jesseb34r15mo ago
kobalte has good examples of this pattern and has good typing although that solution is a little different in kobalte it is done with index files
// ~/components/Card/index.ts
import { Card } as Root from './Card.tsx';
import { CardHeader } as Header from './CardHeader.tsx';

export { Root, Header };
// ~/components/Card/index.ts
import { Card } as Root from './Card.tsx';
import { CardHeader } as Header from './CardHeader.tsx';

export { Root, Header };
thetarnav
thetarnav15mo ago
const Root: ParentComponent<Props> = (props) => {
return //...
};
(Root as any).Header = Header;
export const Card = Root as typeof Root & { header: typeof Header }
const Root: ParentComponent<Props> = (props) => {
return //...
};
(Root as any).Header = Header;
export const Card = Root as typeof Root & { header: typeof Header }
jesseb34r
jesseb34r15mo ago
you don't need to type cast as any there are better ways to do that you could also achieve the index.ts behavior with a default export from a component file like how radix does it actually, they don't default export, they just export an object
jesseb34r
jesseb34r15mo ago
jesseb34r
jesseb34r15mo ago
you can also type a composite component and add that to the typing:
const CardHeader: Component<{ title: string }> = (props) => (
<h2>{props.title}</h2>
)

const CardFooter: Component<{ content: string }> = (props) => (
<div>{props.content}</div>
)

type CardComposite = {
Header: typeof CardHeader;
Footer: typeof CardFooter;
}

const Card: Component<CardProps> & CardComposite = (props) => {
/* card component here */
}

Card.Header = CardHeader;
Card.Footer = CardFooter;

export { Card };
const CardHeader: Component<{ title: string }> = (props) => (
<h2>{props.title}</h2>
)

const CardFooter: Component<{ content: string }> = (props) => (
<div>{props.content}</div>
)

type CardComposite = {
Header: typeof CardHeader;
Footer: typeof CardFooter;
}

const Card: Component<CardProps> & CardComposite = (props) => {
/* card component here */
}

Card.Header = CardHeader;
Card.Footer = CardFooter;

export { Card };
this is the nicer way of doing it without typecasting to any
Mathieu
Mathieu15mo ago
Obviously the solution that thetarnav shared is very simple to understand. What you have shown is something that ends up with: export { Root, Header }; I don't see how that allows Card.Header. In the solution of thetarnav there is an assignment: (Root as any).Header = Header; which makes it clear I can now call Card.Header
jesseb34r
jesseb34r15mo ago
in the index.ts example, you are exporting an object as default from a folder so you do a default import and it gives you the object
Mathieu
Mathieu15mo ago
Ah right
jesseb34r
jesseb34r15mo ago
// ~/components/Card/index.ts
import { CardHeader } as Header from './card-header.tsx';
import { CardRoot } as Root from './card-root.tsx';

export { Root, Header };


// ~/routes/index.tsx
import Card from '~/components/Card'

export default function HomePage() {
return (
<Card.Root>
<Card.Header>{/* content */}</Card.Header>
{/* content */}
</Card.Root>
)
}
// ~/components/Card/index.ts
import { CardHeader } as Header from './card-header.tsx';
import { CardRoot } as Root from './card-root.tsx';

export { Root, Header };


// ~/routes/index.tsx
import Card from '~/components/Card'

export default function HomePage() {
return (
<Card.Root>
<Card.Header>{/* content */}</Card.Header>
{/* content */}
</Card.Root>
)
}
Mathieu
Mathieu15mo ago
so you literally coded it for me haha @jesseb34r one thing. I need to write Card.Root instead of just Card
jesseb34r
jesseb34r15mo ago
yeah, hard to get around that
jesseb34r
jesseb34r15mo ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
jesseb34r
jesseb34r15mo ago
here is an example with code this one allows for named import by doing export const Card = { Root, Header } the composite method I showed might allow that? let me tinker with it for a sec
Otonashi
Otonashi15mo ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Otonashi
Otonashi15mo ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
jesseb34r
jesseb34r15mo ago
yeah the composite method allows for not needing root as does @otonashi9 's examples
jesseb34r
jesseb34r15mo ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
jesseb34r
jesseb34r15mo ago
the reason why your second example works but the first try in the question above didn't is because of
const Card: ParentComponent<Props> = (props) => {}
const Card: ParentComponent<Props> = (props) => {}
vs
function Card(props: ParentProps<Props>) {}
function Card(props: ParentProps<Props>) {}
right? @otonashi9
Otonashi
Otonashi15mo ago
yeah
jesseb34r
jesseb34r15mo ago
that's why you need the composite typing for the arrow function version or i guess you could do it without it
const Card = (props: ParentProps<Props>) => {}
const Card = (props: ParentProps<Props>) => {}
wouldn't type error on assigning Card.Header https://playground.solidjs.com/anonymous/3a771f71-814c-4368-9a40-199961aeae73
Mathieu
Mathieu15mo ago
@otonashi9 one thing is that for all my other components I have default imports, like:
import Button from 'my-ui-lib';
import DatePicker from 'my-ui-lib';
import TextField from 'my-ui-lib';
import { Card } from 'my-ui-lib';
import Button from 'my-ui-lib';
import DatePicker from 'my-ui-lib';
import TextField from 'my-ui-lib';
import { Card } from 'my-ui-lib';
- am I suffering from OCD? - if not, better to move everything as non default export? is it better? - if not, go for thetarnav solution (which allows default import)?
jesseb34r
jesseb34r15mo ago
you can just export default
Otonashi
Otonashi15mo ago
^
Mathieu
Mathieu15mo ago
soz then. Gonna try it out!
jesseb34r
jesseb34r15mo ago
I prefer named imports, idk why, I had a reason when I made the decision to be consistent about that like a year ago I feel like it was a good reason then but I've forgotten it now
Otonashi
Otonashi15mo ago
named imports are easier to refactor, can't remember what else there was
jesseb34r
jesseb34r15mo ago
but I also have to just give up on any OCD organization because my main project is not mine but is a joint project with someone else and they really want imports to be full path, no relative, and use the organization/repo/* syntax except that in this case the org and repo both have the same name it's ugly
jesseb34r
jesseb34r15mo ago
Mathieu
Mathieu15mo ago
Yeah there are advantages: 1. Say you export foo, import { bar } from Foo will fail but import bar from Foo will not fail => better for refactoring => better for searching things in a reliable way 2. You may want to export other things int he future from the same file. => with default export sometimes you'd need to convert to exporting an object. That said, I still like default exports where I'm confident I won't need to refactor names, like a generic "Button" component...
jesseb34r
jesseb34r15mo ago
I think another thing was I wanted it to be easier to import multiple things from the same file and didn't want to have to choose a default export also I like arrow functions and you can't do export default const MyComponent = () => {} you have to do a two liner
const MyComponent = () => {};
export default MyComponent;
const MyComponent = () => {};
export default MyComponent;
which is not nearly so nice as just
export const MyCompenent = () => {};
export const MyCompenent = () => {};
Want results from more Discord servers?
Add your server
More Posts