I am designing a reusable component for breadcrumbs and I am using compound components pattern. I need to insert symbols between each breadcrumb name like how we would do for array elements and use join method but I am not sure how to do that with react elements.
import React from "react";
const BreadcrumbsItem = ({ children }) => (
<p>
{children}
</p>
);
const Breadcrumbs = ({ children }) => <FlexContainer>{children}</FlexContainer>;
Breadcrumbs.Item = BreadcrumbsItem;
export default Breadcrumbs;
Input:
<Breadcrumbs>
<Breadcrumbs.Item>Home</Breadcrumbs.Item>
<Breadcrumbs.Item>Library</Breadcrumbs.Item>
<Breadcrumbs.Item>Data</Breadcrumbs.Item>
</Breadcrumbs>
Output:
CodePudding user response:
Two solutions for you:
Using CSS
Returning fragments
Using inserted content
Using CSS
Assuming this is for the browser and you want the same delimiter (or one of a small set of them), I'd do it with CSS:
.breadcrumb-item .breadcrumb-item::before {
content: " / ";
}
That says "On a .breadcrumb-item
that immediately follows another .breadcrumb-item
(using the next sibling combinator,
, use /
as the "before" pseudo-element text (using the ::before
pseudo-class and content
).
Example:
.breadcrumb-item .breadcrumb-item::before {
content: " / ";
}
<span class="breadcrumb-item">A</span>
<span class="breadcrumb-item">B</span>
<span class="breadcrumb-item">C</span>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
You could use additional classes to choose among delimiters, or even make the content dependent on an attribute:
Example:
.breadcrumb-item .breadcrumb-item::before {
content: attr(data-before);
}
<span class="breadcrumb-item">A</span>
<span class="breadcrumb-item" data-before="*">B</span>
<span class="breadcrumb-item" data-before="/">C</span>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
Returning fragments
Your Breadcrumb.Item
element could return a fragment when it needs to have a delimiter in front of it:
const content = /*...the rendering of the breadcrumb...*/;
if (delimiter) {
return <>{delimiter}{content}</>;
}
return content;
Using inserted content
In the Breadcrumbs
component, you can convert the children
property to an array via React.Children.toArray
:
const childArray = React.Children.toArray(children);
Then you can insert what you like between them, perhaps with flatMap
:
return <div>{childArray.flatMap((crumb, index) => index === 0 ? crumb : [" / ", crumb])}</div>;
You'll need to add keys to those, though (and not using index
unless you know the details of why that's usually not a good idea. How you add the keys and the values you use for them are highly specific to your code, so I'll leave that as an exercise for the reader. :-)
CodePudding user response:
You can use React.Children.forEach
in your Breadcrumbs
component. Something like this:
let Test = (props) => {
let result = [];
React.Children.forEach(props.children, (child, i) => {
result.push(child);
i !== React.Children.count(props.children) - 1 && result.push('/');
});
return <div>{result}</div>;
};
export default function App() {
return (
<div>
<Test>
<div>Hi</div>
<div>Nice to meet</div>
<div>you</div>
</Test>
</div>
);
}