DEV Community

Jacob Gavin
Jacob Gavin

Posted on

React Children for grid views

I sometimes come across scenarios where I want to display some Chips, or Buttons in a grid kind of layout like this
Image description

But I want to keep my component clean and reduced of margin/padding boxes. I don't always know what kind of component will be inside the box and I don't want to add CSS properties for each kind of element either. I usually use reacts Children to wrap the child components with a box component.

Bare in mind that use mui.

The first thing you might come to think about is doing something like this

function MyComponent(): JSX.Element {
    return (
        <CardContent>
            <Box mr={1] mb={1}>
                <Chip label="Expired" />
            </Box>
            <Box mr={1] mb={1}>
                <Chip label="Pending" />
            </Box>
            <Box mr={1] mb={1}>
                <Chip label="Valid" />
            </Box>
       </CardContent>
    );
}
Enter fullscreen mode Exit fullscreen mode

And that works okay but it kind of hides what's important in this component, which is the <Chip /> components. The Material UI <Box /> component is just noise and it also adds extra nesting. And we don't like to much nesting, it's hard to read and even harder to review in a PR.

What we can do instead is to let our <CardContent /> add that <Box /> component around it childrens automatically with a thing called React.Children.

I've added an example from our codebase down below that uses Children which allows you to iterate all the children.

import { Children, isValidElement } from 'react';
// other imports

export default function CardWithPaddedContent(props: Props): JSX.Element {
    const classes = useStyles(props);
    const { children, ...rest } = props;

    const childrenWithPadding = Children.map(children, (child) => {
        if (!isValidElement(child)) {
            return null;
        }
        return <Box mb={1} mr={1}>{child}</Box>;
    });

    return (
        <CardContent {...rest} classes={classes} >
            {childrenWithPadding }
        </CardContent >
    );
}
Enter fullscreen mode Exit fullscreen mode

The key part of this component is the part where I can iterate the children that gets passed to this component. I have to check if its a valid react element, if it isn't then I just return null otherwise I return the child wrapped in Material UIs <Box /> component with some bottom and right margin

Now we can go back to our original component and refactor it like this

function MyComponent(): JSX.Element {
    return (
        <CardWithPaddedContent>
            <Chip label="Expired" />
            <Chip label="Pending" />
            <Chip label="Valid" />
       </CardWithPaddedContent>
    );
}
Enter fullscreen mode Exit fullscreen mode

And voilà! It looks so much cleaner and the DX get so much better because you can more easily see what's going on in this component.

Why don't add css styling to the <Chip /> component you might wonder
Well, I like to keep the small "dumb" component as dumb as they possible can be. They don't know in which context they will be used and adding margin/padding to them in this scenario will probably make them more complex.

Just add a padding/margin prop to the chip then
Could do that as well but I don't like that. A component should have as few props as possible or you can end up in a "aprocalypse" where small component have so many props that you after a while don't know whats what.

Oldest comments (0)