Pass refs to child components using the function as a child pattern
Written byPhuoc Nguyen
Created
13 Oct, 2023
Tags
Function as a child pattern, React callback refs
In the previous post, we learned about using a callback ref to build simple container queries. That's cool and all, but what if we want to use those queries in other parts of our app? That's where creating reusable components comes in.
Reusable components are a fundamental part of React development. They allow us to write code that's modular and easy to maintain, and can be used across different parts of our app. By creating reusable components, we can write less code and avoid bugs.
To do this, we'll use a pattern called functions as a child. But before we dive into that, let's first understand what this pattern is all about.
#Introduction to the function as a child pattern
When working with children props in React, you'll typically encounter components or simple strings/numbers. But did you know that the children prop can also be a function?
Enter the Function as a child (FAAC) pattern. This popular technique in React allows for more dynamic communication between parent and child components. Instead of passing down props directly, the parent component passes down a function as a child. The child component can then call this function with any necessary data, providing more flexibility and interactivity between components.
This pattern is particularly useful when the child component needs to interact with its parent in some way. By passing a function as a child, the parent retains control over what data is being passed down, while still allowing the child to manipulate and use that data in a meaningful way.
The Function as a child pattern is a popular technique used in many libraries and frameworks, including React Router, a widely used routing library for React applications. This pattern allows the router to pass its routing information down to its child components, giving them access to the router's state and enabling them to navigate between different routes.
tsx
<Router>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/topics">Topics</Link></li>
</ul>
</nav>
<Switch>
{/* Render some UI here based on the current URL */}
<Route exact path="/">
{({ match }) => (
// Render some UI here if the current URL matches "/"
)}
</Route>
{/* Render some UI here based on the ID parameter */}
<Route path="/topics/:topicId">
{({ match }) => (
// Render some UI here if the current URL matches "/topics/:topicId"
)}
</Route>
{/* Render some UI here for any other URLs */}
<Route path="/">
{({ match }) => (
// Render some UI here if no other routes match
)}
</Route>
</Switch>
</Router>
This example shows how React Router passes routing information to its child components using a "Function as a Child". The
`Route`
component takes a function as its child, which gets an object with information on whether the current route matches the specified path. This makes it possible to render UI conditionally based on the current URL.#Passing a callback ref to child components
Now that you understand the basic concepts of the Function as a Child pattern, let's see how we can use it to pass a callback reference to a child component.
To do this, let's take a step back to the example in the previous section. We'll create a reusable component named
`SizeTracker`
that passes the callback reference and internal state to children.tsx
export const SizeTracker = ({ children }) => {
const [width, setWidth] = React.useState(0);
const trackSize = (ele) => {
// ...
};
return children({
ref: trackSize,
width,
});
};
The
`SizeTracker`
component we've just created uses the Function as a Child pattern to pass down its internal state and the callback reference to its children.This approach allows us to use
`SizeTracker`
in a more flexible way, as it doesn't make any assumptions about how we want to render our components. Instead, it simply provides us with the necessary information and leaves the rendering logic up to us.Here's an example of how we can use
`SizeTracker`
with a child component:tsx
<SizeTracker>
{
({ ref, width }) => (
<div ref={ref}>
{/* Render some UI here based on the current width */}
</div>
)
}
</SizeTracker>
In this example, we're passing a function as a child to
`SizeTracker`
. The function receives an object with two properties: `ref`
and `width`
. We can then use these properties in our child component to track the size of the element and render UI based on its width.Below you'll find the sample code that performs the same functions as described in the previous post:
tsx
<SizeTracker>
{
({ ref, width }) => (
<div ref={ref}>
<div
style={{
columnCount: width < 200 ? 1 : (width < 400 ? 2 : 3)
}}
>
...
</div>
</div>
)
}
</SizeTracker>
#Demo
Check out the demo below to see how the number of columns adjusts. Try dragging the element on the right side to move it left or right, and watch the size of the container change, updating the layout accordingly. Go ahead and give it a try!
Using the Function as a child pattern allows us to create more flexible and reusable components that can be used in different contexts without assuming how they should be rendered. This leads to more maintainable and composable code, which is especially important for large-scale projects.
Questions? 🙋
Do you have any questions about front-end development? If so, feel free to create a new issue on GitHub using the button below. I'm happy to help with any topic you'd like to learn more about, even beyond what's covered in this post.
While I have a long list of upcoming topics, I'm always eager to prioritize your questions and ideas for future content. Let's learn and grow together! Sharing knowledge is the best way to elevate ourselves 🥷.
Recent posts ⚡
Newsletter 🔔
If you're into front-end technologies and you want to see more of the content I'm creating, then you might want to consider subscribing to my newsletter.
By subscribing, you'll be the first to know about new articles, products, and exclusive promotions.
Don't worry, I won't spam you. And if you ever change your mind, you can unsubscribe at any time.
Phước Nguyễn