In the world of React JS, render callbacks are emerging as a powerful alternative to higher-order components (HOCs). But just what is a render callback? And why should you avoid using one of the most popular implementations of a render callback known as Function as Child Components? I’ll introduce what I believe are two better solutions that I call “Function as Prop Components” and Component Injection.

What is a render callback?

First some background. In JavaScript, functions are first-class citizens. They are merely objects and thus you can pass them around at will, even as parameters to other functions. This is one of the most powerful features of the language.

Take a look at the example below. Notice that we create a function foo which takes a callback function as a parameter. When we call foo, it turns around and “calls back” to the passed-in function.

const foo = (hello) => {
  return hello('foo');
};

foo((name) => {
  return `hello from ${name}`;
});

// hello from foo

As you can see, foo used the callback function to complete a portion of a string. In the React world, a render callback works the same way, but returning a portion of the rendered markup.

What is a Function as Child Component?

A Function as Child Component (or FaCC) is a pattern that lets you you pass a render function to a component as the children prop. It exploits the fact that you can change what you can pass as children to a component. By default, children is of type ReactNodeList (think of this as an array of JSX elements). It’s what we all are used to when placing JSX within other tags.

When you use a FaCC, instead of passing JSX markup, you assign children as a function.

<Foo>
  {(name) => <div>`hello from ${name}`</div>}
</Foo>

In this example, the Foo component would look something like this.

const Foo = ({ children }) => {
  return children('foo');
};

As you can see, this is nearly identical to the initial example that I gave of a callback function.

There is an excellent article that explains Function as Child Components beautifully. While I think there are better alternatives to FaCCs, the author does a fine job of explaining them.

A real-world example of a FaCC

Now let’s look at a more advanced example of a FaCC. This one is based on reactpatterns example of a render callback.

class WindowWidth extends React.Component {
  constructor(props) {
    super(props);
    this.state = { width: 0 };
  }

  componentDidMount() {
    this.setState({ width: window.innerWidth });
    window.addEventListener(
      'resize',
      ({ target }) => {
        this.setState({ width: target.innerWidth });
      }
    );
  }

  render() {
    const { width } = this.state;
    return this.props.children(width);
  }
}

Then you could use it as follows.

<WindowWidth>
  {width => <div>window is {width}</div>}
</WindowWidth>

As you can see above, children is “overloaded” and passed to WindowWidth as a function instead of being a ReactNodeList as nature intended. The WindowWidth component’s render method calls this.props.children (passing it width), which returns rendered JSX.

The real power of render callbacks can be seen in this example. WindowWidth will do all of the heavy lifting, while exactly what is rendered can change, depending on the render function that is passed.

Not all rectangles are squares

It’s important not confuse Function as Child Components with render callbacks. A FaCC is simply one implementation of a render callback. There are other ways to implement render callbacks. In other words, a FaCC is a type of render callback, but not all render callbacks are FaCCs.

FaCCs are an anti-pattern. There, I said it.

What?! How can that be? FaCCs are loved and embraced by many in the React community. How can it be an anti-pattern? Allow me make my case by taking a step back.

“There are only two hard things in Computer Science: cache invalidation and naming things.”

– Popularly attributed to Phil Karlton

Let’s say that you had a function that took two numbers, added them together and returned the result. What would you call that function? You might call it add, or maybe sum.

const add = (a, b) => {
  return a + b;
};

add(2, 2);

Clean coding practices state that you use descriptive names for properties, variables, functions, etc. I hope that we can all agree with this concept. You would never name your add function above badger (although that would be cool) or tapioca or even children, would you?

const children = (a, b) => {
  return a + b;
};

children(2, 2);

Of course you wouldn’t. That would be silly, confusing, and not very self-documenting. Then why in the world would you ever use a prop named children to pass a render callback function? I can only imagine seeing this used for the first time in a pull request. Not in my organization, fella. Rejected!

Using a pattern that goes against what we all consider to be a best practice is called an anti-pattern. By definition, then, FaCCs are an anti-pattern.

Function as Prop Component

I’ll remind you that passing a render callback function to a component is not the issue. The issue is that the FaCC implementation chose to use the prop children.

So how could we pass a render callback function to a component in a clean manner? You would need to name your prop something meaningful. Here’s how we could change our Foo example above to pass a function as a prop.

<Foo hello={(name) => <div>`hello from ${name}`</div>} />

And here’s another example, except hello is created outside of the JSX (a better practice, IMO).

const hello = (name) => {
  return <div>`hello from ${name}`</div>;
};
<Foo hello={hello} />

And this time, Foo makes a lot more sense.

const Foo = ({ hello }) => {
  return hello('foo');
};

Notice how this is much more descriptive? The code is self-documenting. You can say to yourself, “Foo calls a hello function,” instead of “Foo calls something; I don’t remember what.”

Component Injection - A better solution

We’ve already seen that naming your props is important. But instead of calling a function to render, I’d like to propose that, in many cases, you should pass a component. This allows you to use the more expressive JSX syntax. It’s called Component Injection because you pass (or inject) a component into another component.

Here’s the altered Foo solution.

const Hello = ({ name }) => {
  return <div>`hello from ${name}`</div>;
};
<Foo Hello={Hello} />

Things look pretty much the same as in the Function as Prop Component example above, except we capitalize Hello because convention calls for us to capitalize the first letter of a component name. We also pass in props, an object, instead of a single string parameter, but those are the only differences. This should all look very familiar to you.

And our Foo component looks a lot more like a traditional React component.

const Foo = ({ Hello }) => {
  return <Hello name="foo" />;
};

Instead of calling a function to render, it uses standard JSX composition. Refreshing, huh?

The only thing that might be different from what you might be used to is that instead of importing Hello, we inject it (i.e., pass it as a prop).

Injecting your dependencies is a powerful side effect of this technique that allows for easier run-time altering of components and increased ease of mocking tests.

Need more examples? Here’s the more advanced solution shown above, altered for Component Injection. Everything with WindowWidth remains the same except for the render method:

render() {
  const { width } = this.state;
  const { Width } = this.props;
  return <Width width={width} />;
}

…as well as how you use it.

<WindowWidth Width={DisplayWindowWidthText} />
const DisplayWindowWidthText = ({ width }) => {
  return <div>window is {width}</div>;
};

As you can see, the DisplayWindowWidthText component is “injected” into WindowWidth as a prop named Width.

We could even pass a different component and get a completely different rendered output – again, thanks to the power of render callback.

<WindowWidth Width={DisplayDevice} />
const DisplayDevice = ({ width }) => {
  let device = null;
  if (width <= 480) {
    device = 'mobile';
  } else if (width <= 768) {
    device = 'tablet';
  } else {
    device = 'desktop';
  }
  return <div>you are using a {device}</div>;
};

In closing…

In all three methods shown here – Function as Child Component, Function as Prop Component, and Component Injection – the means to render is passed into the component, and the component “calls back” to perform the rendering. All three are implementations of a render callback.

It’s important to note that my opinion on FaCCs is not universally shared. Many people in the industry, and even some members of my own development team, don’t see the issue with them. However, it’s clear to me, and many respected industry leaders, that re-purposing children goes against one of the most basic things we’re taught in Computer Science; that is, “Choose meaningful names for your variables.”

I can only imagine that overloading children by passing a function was probably discovered by someone who said, “Cool hack! I wonder what can I do with this?” Don’t be a hack. Reject the use of Function as Child Components.