S
SolidJS•3d ago
Paul

Determine component type

With react it's possible to tag a component with a type:
<Card withBorder>
<Card.Section inheritPadding py="md" withBorder>
Card section 1
</Card.Section>
...
</Card>
<Card withBorder>
<Card.Section inheritPadding py="md" withBorder>
Card section 1
</Card.Section>
...
</Card>
In the card component:
Card.Section = CardSection;
Card.Section = CardSection;
Now I can find that Card.Section, again in card component:
if (typeof child === 'object' && child && 'type' in child && child.type === CardSection) {
if (typeof child === 'object' && child && 'type' in child && child.type === CardSection) {
Unfortunately, it doesn't seem possible with solidjs. What I have done to determine whether a component is a Card.Section. Within CardSection.tsx
<Box
ref={ref}
mod={[{ 'with-border': local.withBorder, 'inherit-padding': local.inheritPadding, 'card-section': true }, local.mod]}
<Box
ref={ref}
mod={[{ 'with-border': local.withBorder, 'inherit-padding': local.inheritPadding, 'card-section': true }, local.mod]}
I'm setting: 'card-section': true Then to find it:
if ((item as any)?.dataset?.cardSection === 'true') {
if ((item as any)?.dataset?.cardSection === 'true') {
But is there a more idiomatic way?
9 Replies
Dakotys
Dakotys•3d ago
It depends on what your goal is, why do need to find reference to Card.Section? Usually the preferred way of dealing with sub components is to use createContext. Then you can avoid things like looping over them and resoving them with children().
Paul
PaulOP•3d ago
An example is:
<Card withBorder unstyled>
<Card.Section inheritPadding py="md" withBorder>
Card section 1
</Card.Section>
<div>Content 1</div>
<Card.Section inheritPadding withBorder>
Card section 2
</Card.Section>
<div>Content 2</div>
<Card.Section inheritPadding withBorder>
Card section 3
</Card.Section>

<Card.Section inheritPadding withBorder>
Card section 4
</Card.Section>
</Card>
<Card withBorder unstyled>
<Card.Section inheritPadding py="md" withBorder>
Card section 1
</Card.Section>
<div>Content 1</div>
<Card.Section inheritPadding withBorder>
Card section 2
</Card.Section>
<div>Content 2</div>
<Card.Section inheritPadding withBorder>
Card section 3
</Card.Section>

<Card.Section inheritPadding withBorder>
Card section 4
</Card.Section>
</Card>
Some content can be Card.Sections or divs or other components entirely. I'm using children() already 😄 this is what I have so far:
c.forEach((child, i) => {
if ((child as any)?.dataset?.cardSection === 'true') {
const childProps = (child as any).props || {};
const childChildren = (child as any).children;
result.push(
<CardSection
{...childProps}
data-first-section={i === 0 || undefined}
data-last-section={i === c.length - 1 || undefined}
>{childChildren}</CardSection>
);
} else {
result.push(child);
}
})
c.forEach((child, i) => {
if ((child as any)?.dataset?.cardSection === 'true') {
const childProps = (child as any).props || {};
const childChildren = (child as any).children;
result.push(
<CardSection
{...childProps}
data-first-section={i === 0 || undefined}
data-last-section={i === c.length - 1 || undefined}
>{childChildren}</CardSection>
);
} else {
result.push(child);
}
})
REEEEE
REEEEE•3d ago
This isn't a very idiomatic pattern in solid Since there's no vdom, there's not really support for this exactly to mimic React
Paul
PaulOP•3d ago
That's what GPT has told me. But I was hoping someone with more experience would know of a solution.
REEEEE
REEEEE•3d ago
Just as a refresher for myself, what's the problem that this solves? I haven't used React in a bit. Is it just to add additional props to the children?
Paul
PaulOP•3d ago
The intent is for the card component to loop the children. Any child that is marked as CardSection has then additional props added to it. Then all children collected and are children to another component in the tree.
const _children = Children.toArray(children);
const content = _children.map((child, index) => {
if (typeof child === 'object' && child && 'type' in child && child.type === CardSection) {
return cloneElement(child, {
'data-first-section': index === 0 || undefined,
'data-last-section': index === _children.length - 1 || undefined,
} as any);
}

return child;
});
return (
<CardProvider value={{ getStyles }}>
<Paper ref={ref} unstyled={unstyled} {...getStyles('root')} {...others}>
{content}
</Paper>
</CardProvider>
);
const _children = Children.toArray(children);
const content = _children.map((child, index) => {
if (typeof child === 'object' && child && 'type' in child && child.type === CardSection) {
return cloneElement(child, {
'data-first-section': index === 0 || undefined,
'data-last-section': index === _children.length - 1 || undefined,
} as any);
}

return child;
});
return (
<CardProvider value={{ getStyles }}>
<Paper ref={ref} unstyled={unstyled} {...getStyles('root')} {...others}>
{content}
</Paper>
</CardProvider>
);
What I have works fine. I just wanted to double check if there was a better way.
REEEEE
REEEEE•3d ago
You could add a "registerSection" function to the context that increments an index and the sections just use the index to set the data attributes themselves Probably also need a totalSections signal actually in the card
// Root
const [totalSections, setTotalSections] = createSignal(0)

const context = {
registerSection: () => setTotalSections(p => p + 1),
unregisterSection: () => setTotalSections(p => Math.max(p - 1, 0)),
totalSections
}


...

// Section
const context = useCardContext()

const index = context.registerSection()

onCleanup(context.unregisterSection)

...

<Box data-first-section={index === 0 || undefined} data-last-section={context.totalSections() - 1 === index || undefined} />
// Root
const [totalSections, setTotalSections] = createSignal(0)

const context = {
registerSection: () => setTotalSections(p => p + 1),
unregisterSection: () => setTotalSections(p => Math.max(p - 1, 0)),
totalSections
}


...

// Section
const context = useCardContext()

const index = context.registerSection()

onCleanup(context.unregisterSection)

...

<Box data-first-section={index === 0 || undefined} data-last-section={context.totalSections() - 1 === index || undefined} />
Paul
PaulOP•3d ago
Thanks, but I'm going to stick with my code.
having 'card-section': true in the CardSection means it's totally transparent and the rest of the code stays the same
Dakotys
Dakotys•3d ago
Just bear in mind that if someone uses Show or For inside the Card component it will trigger a complete recreation of CardSelections and that may lead to some undesired behavior like focus loosing and such.

Did you find this page helpful?