Using a React Component's function from its Parent
Tags: react, javascript, webdev
Published: 2021-05-22 02:20:24

React offers an awesome toolset that allows you to effectively break up and modularize your applications into smaller pieces that work together. This allows us developers to breakout functionality and keep it in one location. 

While this way of architecting allows for amazing levels of flexibility and maintainability, we all will eventually hit this scenario: *What if I want one custom React component to trigger a function that lives in one of its custom Child components?*

Using React's `forwardRef` API along with the built-in hook `useImperativeHandle`, this is simple to do!

*This tutorial assumes you have some basic knowledge of React and how to use it.*

---

## Setting up our Components

To demonstrate, we're going to build a simple component that presents you with some color options and a box. Clicking on the color buttons will change the color of the box.

![Box changing colors when buttons are clicked](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zljct5vif4s0qya30pu7.gif)

The header and buttons of our component will live in the Parent component, while the color-shifting box and the functionality to change the box's color will be held in the Child component. Here's what that looks like:

```jsx
import { useState } from 'react'
const Parent = () => {
    return (
        <div className="flex flex-col gap-4 min-h-screen bg-gray-200 justify-center items-center">
            <h2 className="text-gray-500 text-2xl font-bold text-30">What color should the box be?</h2>
            <div className="flex justify-between w-80">
                <button className="bg-blue-400 py-2 px-4 focus:outline-none rounded-xl text-white font-bold">Blue</button>
                <button className="bg-green-400 py-2 px-4 focus:outline-none rounded-xl text-white font-bold">Green</button>
                <button className="bg-red-400 py-2 px-4 focus:outline-none rounded-xl text-white font-bold">Red</button>
                <button className="bg-yellow-400 py-2 px-4 focus:outline-none rounded-xl text-white font-bold">Yellow</button>
            </div>
            <Child/>
        </div>
    )
}

const Child = () => {
    const [ color, setColor ] = useState('bg-blue-300')
    const changeColor = color => {
        setColor(color)
    }
    return <div className={`w-40 h-40 transition-colors duration-900 ease-in-out rounded-2xl ${color}`}></div>
}
```

>NOTE: I'm using [TailwindCSS](https://tailwindcss.com/) for some quick styling

There's nothing too crazy going on here, just rendering out our Parent and Child. The Child has a function to update the color of its box, and some state to hold that setting. For now the buttons don't do anything, the box is blue... boring! Let's bring this thing to life!


> NOTE: I'm using this as an example because it's easy to understand and visualize the result. In a real-world setting, you'd be better off passing the color through to the Child component as a prop.

---

## Reference the Child

First thing's first, we need to somehow reference the Child component to get access to its properties. React's [`useRef`](https://reactjs.org/docs/hooks-reference.html#useref) hook does exactly that. To create a reference to the Child component, we'll need to import that hook from `react`, create a reference, and apply that reference to the component.

```jsx
// Added useRef to our imports
import { useState, useRef } from 'react'

const Parent = () => {
    // Set up our reference
    const boxRef = useRef(null)

    return (
        <div className="flex flex-col gap-4 min-h-screen bg-gray-200 justify-center items-center">
            <h2 className="text-gray-500 text-2xl font-bold text-30">What color should the box be?</h2>
            <div className="flex justify-between w-80">
                <button onClick={() => boxRef.current.changeColor('bg-blue-300')} className="bg-blue-400 py-2 px-4 focus:outline-none rounded-xl text-white font-bold">Blue</button>
                <button onClick={() => boxRef.current.changeColor('bg-green-300')} className="bg-green-400 py-2 px-4 focus:outline-none rounded-xl text-white font-bold">Green</button>
                <button onClick={() => boxRef.current.changeColor('bg-red-300')} className="bg-red-400 py-2 px-4 focus:outline-none rounded-xl text-white font-bold">Red</button>
                <button onClick={() => boxRef.current.changeColor('bg-yellow-300')} className="bg-yellow-400 py-2 px-4 focus:outline-none rounded-xl text-white font-bold">Yellow</button>
            </div>
            {/* Apply the reference to our component */}
            <Child ref={boxRef}/>
        </div>
    )
}
```

We now have a reference set up that *should* give us access to the properties of the Child. This reference has a property named `.current` that is set to value of the DOM node of the component it is attached to, giving it access to its properties. 

I went ahead and added the click-handlers on each button to trigger the `changeColor` function in the Child component. Everything seems to be hooked up so we should be good to go, right? Let's try it out:

![Forward Ref Error Message](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zk3nmli04p665ofclyhm.png)

Oof, it blew up! 💥 What's going on? 

The reason this won't work, and the thing that makes this process tricky, is that the `ref` property on our `<Child/>` component is not a normal "prop". React handles `ref` differently than it handles most other props and doesn't pass it through to the Child in the props object. 

---

## `forwardRef` To The Rescue

To get this to work properly, we need to "forward" our ref to the Child component. Luckly, React has a nice API called `forwardRef` that allows exactly that. 

To use this API, we need to import it from `react` and wrap our Child component in the `forwardRef` function. This function takes in  `props` and `ref` parameters and returns the Child component. 

```jsx
// Added forwardRef to the import list
import { forwardRef, useState, useRef } from 'react'
const Child = forwardRef((props, ref) => {
    const [ color, setColor ] = useState('bg-blue-300')
    const changeColor = color => {
        setColor(color)
    }
    return <div className={`w-40 h-40 transition-colors duration-900 ease-in-out rounded-2xl ${color}`}></div>
})
```

This will pass along our ref to the Child component, but now we need to expose our `changeColor` function to the Parent component through that ref. To do that we will need to use [`useImperativeHandle`](https://reactjs.org/docs/hooks-reference.html#useimperativehandle), a hook that React provides. This hook takes in a `ref` param and a function that allows you to expose custom properties to the Parent through that ref. Here it is in action:

```jsx
// Added useImperativeHandle to our imports
import { forwardRef, useState, useRef, useImperativeHandle } from 'react'
const Child = forwardRef((props, ref) => {
    const [ color, setColor ] = useState('bg-blue-300')
    useImperativeHandle(ref, () => ({
      changeColor: color => {
          setColor(color)
      }
    }))
    return <div className={`w-40 h-40 transition-colors duration-900 ease-in-out rounded-2xl ${color}`}></div>
})
```

We now have forwarded our ref into the Child component and customized the the instance that is exposed to the Parent component, giving it access to a function that will update the Child's state to change the color of our box. 

Save that and give 'er a go!

![Box changing colors when buttons are clicked](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zljct5vif4s0qya30pu7.gif)

Fancy! Our "handle" into the Child component is accessible from our Parent component and allows us to update the child's state through the function we've exposed via that "handle".


Here's a look at both completed functions:

```jsx
import { forwardRef, useState, useRef, useImperativeHandle } from 'react'

const Parent = () => {
    // Set up our reference
    const boxRef = useRef(null)

    return (
        <div className="flex flex-col gap-4 min-h-screen bg-gray-200 justify-center items-center">
            <h2 className="text-gray-500 text-2xl font-bold text-30">What color should the box be?</h2>
            <div className="flex justify-between w-80">
                <button onClick={() => boxRef.current.changeColor('bg-blue-300')} className="bg-blue-400 py-2 px-4 focus:outline-none rounded-xl text-white font-bold">Blue</button>
                <button onClick={() => boxRef.current.changeColor('bg-green-300')} className="bg-green-400 py-2 px-4 focus:outline-none rounded-xl text-white font-bold">Green</button>
                <button onClick={() => boxRef.current.changeColor('bg-red-300')} className="bg-red-400 py-2 px-4 focus:outline-none rounded-xl text-white font-bold">Red</button>
                <button onClick={() => boxRef.current.changeColor('bg-yellow-300')} className="bg-yellow-400 py-2 px-4 focus:outline-none rounded-xl text-white font-bold">Yellow</button>
            </div>
            {/* Apply the reference to our component */}
            <Child ref={boxRef}/>
        </div>
    )
}

const Child = forwardRef((props, ref) => {
    const [ color, setColor ] = useState('bg-blue-300')
    useImperativeHandle(ref, () => ({
      changeColor: color => {
          setColor(color)
      }
    }))
    return <div className={`w-40 h-40 transition-colors duration-900 ease-in-out rounded-2xl ${color}`}></div>
})
```
---

## Conclusion

Using React's `forwardRef` API and `useImperativeHandle` hook, we gain the flexibility to allow for even greater component interactions, adding to the awesome flexibility of the React library. While the example in this article was a bit overkill and added a sorta unnecessary level of complexity to an otherwise simple component, these concepts can be extremely useful when building Component libraries with Alerts, Modals, etc...

Thanks so much for giving this a read, I hope it was helpful!

If you liked this, be sure to follow me on [Twitter](https://twitter.com/sabinthedev) to get updates on new articles I write!