So I have two components: Tag and TagGroup. When Tag component is used alone, I want that component to use <div>
tag and when Tag is wrapped inside of TagGroup, which means more than 1 Tag is being used, then I want it to use <li>
tag. With my current design, I used context to pass prop groupUpTags
to Tag component. When groupUpTags
is true then Tag component will use <li>
tag, if false, <div>
will be used. Currently, it doesn't even work properly as whether I wrap Tag component under TagGroup or not <li>
tag is always being used. My question is, is there a way to get Tag component to render <li>
tag when wrapped inside TagGroup without using any props and render <div>
tag when used alone? My current design look little too messy.
https://codesandbox.io/s/tag-group-s07oyh?file=/src/App.tsx
App.tsx
import "./styles.css";
import React from "react";
import { TagGroupContextInterface, TagGroupContext } from "./TagGroupContext";
export interface TagGroupInterface extends TagGroupContextInterface {
children: React.ReactNode;
}
const Tag = () => {
const context = React.useContext(TagGroupContext);
return context.groupUpTags ? (
<li className="tag">Tag</li>
) : (
<div className="tag">Tag</div>
);
};
const TagGroup = ({ groupUpTags = true, children }: TagGroupInterface) => {
const context = React.useMemo(
() => ({
groupUpTags
}),
[groupUpTags]
);
return (
<TagGroupContext.Provider value={context}>
<div>
<ul>{React.Children.map(children, (child) => child)}</ul>
</div>
</TagGroupContext.Provider>
);
};
export default function App() {
return (
<div className="App">
<TagGroup>
<Tag />
<Tag />
<Tag />
</TagGroup>
</div>
);
}
TagGroupContext.tsx
import React from "react";
export interface TagGroupContextInterface {
groupUpTags?: boolean;
}
const defaultTagGroupContext: TagGroupContextInterface = {
groupUpTags: true
};
export const TagGroupContext = React.createContext<TagGroupContextInterface>(
defaultTagGroupContext
);
A Tag component should probably always return a simple tag in a div or a span whatever you need for the stand alone tag to work.
I would then add the li inside the list component since thats the moment you need the li. when you code a Tag it shouldn't know about the parents.
Probably something like this in your Tag Group
{Children.map(children, (child, i) => {
<li key={i}>{child}</li>;
})}
note: don't use "i" as a key its not reliable. Use a uid from the tag itself.