'Iterate over children, find last element
I work on breadcrumb component and I iterate over children like:
renderChildren = () => {
const { children, divider } = this.props;
return (
React.Children.map(children, child => (
<>
{child}
{divider}
</>
))
);
}
Divider is between elements in breadcrumb and on last element in breadcrumb I do not want to put divider. One picture, I do not want to display last chevron (divider):
Solution 1:[1]
renderChildren = () => {
const { children, divider } = this.props;
return (
React.Children.map(children, (child, index) => (
<>
{child}
{(index < children.length - 1) && divider}
</>
))
);
}
Update:
const children: React.ReactNode Object is possibly 'null' or 'undefined'.ts(2533)
Is out of scope of this answer, but to handle this scenario
renderChildren = () => {
const { children, divider } = this.props;
if (children) { // this fixes error above
return (
React.Children.map(children, (child, index) => (
<>
{child}
{(index < children.length - 1) && divider}
</>
))
);
}
return null
}
That error comes because you defined your prop as children?: ReactNode
, because it's optional TypeScript secures you from undefined values
Solution 2:[2]
You could use index and check if it equals to the length of the children
array:
renderChildren = () => {
const { children, divider } = this.props;
return (
React.Children.map(children, (child, index) => (
<>
{child}
{index !== children.length-1 && divider}
</>
))
);
}
Solution 3:[3]
Using children.length
is wrong and not typesafe; it simply 'happens to work', and may be broken in future versions of react. ReactNode
can be null, which will break this even right now.
You should use the react top level api to process children;
use
React.Children.toArray
to determine the number of child elements.use
React.Children.map
to map the child elements to new elements.
Like this:
export const Breadcrumb = (props: { divider: () => ReactNode, children: React.ReactNode }) => {
const childCount = React.Children.toArray(props.children).length;
return (<>
{React.Children.map(props.children, (child, index) => {
if (React.isValidElement(child)) {
const isLastChild = index === childCount - 1;
if (isLastChild) {
return React.cloneElement(child, {style: {border: "1px solid #ff0000"}})
}
return (<>
{child}
{props.divider()}
</>);
}
})};
</>);
}
eg.
<Breadcrumb divider={() => <span> > </span>}>
<span>A</span>
<span>B</span>
<span>C</span>
</Breadcrumb>
Solution 4:[4]
THIS WORKS FOR ME BETTER
return Children.map(children, (child, index) => {
const isLastChildrenIndex = index === Children.count(children) - 1;
return (
<>
{child}
{isLastChildrenIndex && <Divider />}
</>
);
});
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Clarity |
Solution 3 | Doug |
Solution 4 | Omar Borji |