DEV Community

loading...
Cover image for Power up your renders with render callbacks in React

Power up your renders with render callbacks in React

moimikey profile image Michael Scott Hertzberg Updated on ・1 min read

You can't feel the power quite yet, but we're about to power up our rendering skills by passing a function as a child:

<React.Fragment>
    {() => {}}
</React.Fragment>

Blep.

OvbGwwI.jpg

Well, that wasn't very exciting. Not yet at least. What we need first is an idea. Let's build a share toolbar! It will be quite simple. We're going to have a row of icons: One to share to Facebook; one to share to Twitter; one to send via email; and one to copy a permalink. Sounds pretty simple. I've seen these on many sites.

function ShareToolbar(props) {
    return (
        <div>
            <a href="javascript:">Facebook</a>
            <a href="javascript:">Twitter</a>
            <a href="javascript:">Email</a>
            <a href="javascript:">Permalink</a>
        </div>
    );
}

It's looking a little like something now. Not exactly anything special. But, I want to do a little bit more here. I want to be able to style each of the links. Possibly turn them into social icon buttons with an SVG icon. I may want them to have calculated hyperlinks. I also want to create a "drawer" for the permalink, so we can visually see a short url next to the cute buttons.

giphy-downsized.gif

const Item = (props) =>
    <Item onClick={props.onClick} href={props.href}>{props.children}</Item>;

const Button = (props) =>
    <Button>{props.children}</Button>;

const Drawer = (props) =>
    props.isOpen ? <Drawer>{props.children}</Drawer> : null;

function ShareToolbar(props) {
    return (
        <React.Fragment>
            {props.children ? props.children({
                    Item,
                    Button,
                    Drawer
            }) : null}
        </React.Fragment>
    );
}

export default ShareToolbar

Well that got complex real fast. But, at least it's readable! (I hope). We're doing exactly what we did in the first example, however, instead of returning an empty object, or in the second example, where we return JSX, we're calling the children prop as a function (if it exists)

Why does this sentence almost not make sense?

calling the children prop as a function

That's probably because it's just difficult at first to visualize, until we develop it from an example. Let's go the opposite direction and instead design how we want to use the component:

<ShareToolbar>
    {({ Item, Button, Drawer }) => {
        return (
            <Toolbar>
                <Item href='https://facebook.com'>
                    <Button>
                        <Icon name='facebook' />
                    </Button>
                </Item>
                <Item href='https://twitter.com'>
                    <Button>
                        <Icon name='twitter' />
                    </Button>
                </Item>
                <Item href='https://gmail.com'>
                    <Button>
                        <Icon name='mail' />
                    </Button>
                </Item>
                <Item onClick={() => {
                    copy('https://google.com');
                    alert('copied to clipboard.');
                }}>
                    <Button>
                        <Icon name='link' />
                    </Button>
                    <Drawer isOpen>
                        https://google.com
                    </Drawer>
                </Item>
            </Toolbar>
        );
    }}
</ShareToolbar>

This reads much clearer (I hope). What we're looking at is psuedo code, however, with the power of styled-components, the naming conventions can actually remain the same. They're just divs after all.

{({ Item, Button, Drawer }) => { // the render callback, `ShareToolbar.children`
    ...
}}

Remember how we were calling props.children as a function? This is that function, being called. It's just squished in-between the ShareToolbar component.

What makes this pattern powerful, is that it allows us to abstract the visual hierarchy of the component and provide the ability to dynamically compose the view how we'd like to render it, versus being made to render in one specific way.

You'll notice this pattern starts to make more sense when it comes to abstracting the view layer from the data layer, in which data is passed as values in the render callback, rather than components. You can read more about this in my next post, Using render callbacks to connect non-connected components.

This article was first published on Codementor https://www.codementor.io/moimikey/power-up-your-renders-with-render-callbacks-mb96thj33

Discussion (1)

pic
Editor guide
Collapse
erhankilic profile image
Erhan Kılıç

Final Flash!!!
:)