Function as Child
In a typical React Component, you would render a child as another React Element.
<Parent>
<Children />
</Parent>
In the “child as function” paradigm, instead of returning an element below, we’re using the Toggle component to have encapsulated state that’s common to multiple components. Since this logic is repeated we can abstract it into its own class and call individual instances of it.
class Toggle extends Component {
state = {
open: false,
};
toggleOpen = () => this.setState({ open: !this.state.open });
render() {
return this.props.children(this.state.open, this.toggleOpen);
}
}
Now we have an instance of Toggle we can repeat over and over again.
const Accordion = () => (
<Toggle>
{(open, handleToggle) => (
<div>
<h1>Title<h1>
<span onClick={handleToggle}>Show Details</span>
{open && <Details />}
</div>
)}
</Toggle>
)
So in our Toggle component, we’re returning a function to be the only child, and passing in the state and a handler to that function. Then when we call an instance of it like in the Accordion or the Thumbnail, the state is encapsulated to this instance. This leaves the child component to be a stateless pure piece of UI, not knowing how it gets opened, only that it can be opened or closed.
Also this pattern gets called “Render Props”, where instead of mapping the function to the children
prop, it’s mapped to a prop called render
and called inside the JSX itself versus as a child. Render Props seems to be more of the conventional name for this pattern and now has official documentation, but the idea remains the same.
class Toggle extends Component {
state = {
open: false
}
toggleOpen = () => this.setState({ open: !this.state.open })
render() {
return this.props.render(this.state.open, this.toggleOpen)
}
}
<Toggle
render={(open, handleToggle) => (
// rendered contents.
)}
/>
Why?
You might be wondering: this doesn’t seem much better than just passing down state to a component or moving this into my store’s reducer, so why should I bother? And I would counter: that was an obnoxiously specific question to ask of a blog post, let me see if I can give a simple response.
Our <Toggle />
component is a really simple example and I’ll demonstrate more in a moment, but it outlines a piece of UI logic that you only need to write one. Simplifying this logic allows you to compose with this idea versus reproducing it. There are loads and loads of UI components that need to be toggled: checkboxes, modals, accordions, dropdown menus. But all those are in reach by calling a new instance of <Toggle />
.
Declaratively composing UI makes every logic decision verbose. Functionality lives in the Component tree, and when you’re declaring those components or searching for your component later, you’re documenting your choices more clearly.
React gets simpler when you stop adding things to it (ref: “In the Mouth of the Beast”). Using “Just Components” can get you pretty far in a codebase without a lot of effort. In my experience, these patterns make on-boarding developers to a project more streamlined, since everything is being composed of declarative components (think pure functions). The verbosity is easier to take in and keep everyone on the same page. Beyond just that, it’s in a parlance that people who already use React understand: Components.
Using the Pattern
Below we’re using rendering a list of results from a fetch call. We’re using a function as child component simply to set the state of network call and as a child of that instance, calling our list.
const List = ({ results }) => (
<ul>
{results.map((r, i) => (
<li key={i}>{r.body}</li>
))}
</ul>
);
class Fetch extends React.Component {
state = {
data: [],
status: undefined,
};
componentDidMount() {
fetch(this.props.url)
.then((res) => ({ status: res.status, data: res.json() }))
.then((result) => this.setState(result))
.catch((err) =>
console.error(err, "Something else to handled this error like an adult")
);
}
render() {
return this.props.children(this.state.status, this.state.data);
}
}
<Fetch url="https://jsonplaceholder.typicode.com/posts">
{(data, status) =>
status === 200 && results.length >= 0 ? (
<List results={data} />
) : (
<span>{status}</span>
)
}
</Fetch>;
Now every time I need to perform a fetch()
request, I just have to call the component.
A Few More Examples
Just so we’re clear on the fact I’m not making this up, there’s quite a number of use cases for this pattern out in the wild.
React Media
A
<Media />
component listens for matches to a CSS media query and renders stuff based on whether the query matches or not.
<Media query={{ minWidth: 320 }}>
{(matches) => (matches ? `I am at least 320px wide` : `I am not`)}
</Media>
Downshift
Primitives to build simple, flexible, WAI-ARIA compliant React autocomplete/dropdown/select/combobox components
Downshift is one of those amazingly extensible components that you’d think is necessary to use some state management solution or require a lot of setup. I won’t bother with an example snippet, because it’s seriously worth it to go read those docs. Here’s a demo of building a simple autocomplete.
Holen
Declarative fetch in React
<Holen lazy onResponse={handleResponse} url="api.startup.com/users">
{({ fetching, data, fetch, error }) => (
<div>
<button onClick={fetch}>Load Data</button>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
)}
</Holen>
Motion
What react-motion
does is it allows you to be able to interpolate style and puts the updating value through an animation within scope. I could be wrong, but I think this might be the original “Function as Child” component.
import { Motion, spring } from "react-motion";
<Motion defaultStyle={{ x: 0 }} style={{ x: spring(10) }}>
{(interpolatingStyle) => <div style={interpolatingStyle} />}
</Motion>;
Happy composing ⌨️ ⚛️ 👩💻