Expose methods of a component using useImperativeHandle()
Tags: react, tutorial, javascript, webdev
Published: 2023-12-13 13:42:26

Sometimes, we may want to give the parent component more control over the child component by allowing access to specific methods or properties. The `useImperativeHandle()` hook, used in combination with `forwardRef()`, can help us achieve this.

With `useImperativeHandle()`, we can define a set of functions and values that will be accessible on the ref object from outside of the component. This is especially useful when we need to call a method on a child component from its parent, such as when triggering an animation or fetching new data.

Let's say we have a `Slider` component, and we want to expose some methods that allow users to navigate to the previous or next item, or jump to any item. We can use `useImperativeHandle()` to make this happen.

## Introducing the Slider component

Let's keep things simple and work with the `Slider` component we [created](https://phuoc.ng/collection/react-ref/reference-an-element-with-react-use-ref-hook/) earlier. Just to refresh your memory, here's how it works:

{% codesandbox kx9tsk %}

In this example, the `Slider` component includes arrows on the sides for users to navigate to the previous and next items. There are also dots at the bottom to allow users to jump to any item they want.

To better understand the power of `useImperativeHandle`, we've removed the animation of the current dot item, leaving the `Slider` component with basic functionalities. However, it still includes everything for navigation. What if we exposed a few navigation methods, so users have full control of the navigation UI but still have the full advantage of the slider, such as smooth animation when navigating between items?

For example, imagine a custom slider component with several methods for controlling navigation. These methods need to be accessible to the parent component so that it can control the slider navigation.

Let's dive into the next section to get a better understanding of the basics behind `useImperativeHandle()`.

## Understanding the syntax of useImperativeHandle

The `useImperativeHandle()` hook is a powerful tool that takes two arguments. The first argument is the `ref` object we want to assign our functions and values to, while the second argument is a function that returns an object containing those functions and values.

Here's an example of how we can use `useImperativeHandle()` to expose methods of the `Slider` component:

```tsx
interface SliderMethods {
    goToPreviousItem: () => void;
    goToNextItem: () => void;
    jump: (index: number) => void;
};

const Slider = React.forwardRef<SliderMethods>((props, ref) => {
    const goToPreviousItem = () => {
        ...
    };

    const goToNextItem = () => {
        ...
    };

    const jump = (index) => {
        ...
    };

    React.useImperativeHandle(ref, () => ({
        goToPreviousItem,
        goToNextItem,
        jump,
    }));

    return (
        // Render slider UI ...
    );
});
```

In this example, we create an interface called `SliderMethods` that lists all the necessary methods for slider navigation. These methods include `goToPreviousItem()`, `goToNextItem()`, and `jumpToItem()`. We then pass this interface as the first argument to `React.forwardRef`.

To make these methods accessible to the parent component, we use `useImperativeHandle`. This function takes a second argument, which is a function that returns an object with all the necessary methods.

Now, these methods can be accessed on the ref object in the parent component. It's like giving the parent component a key to unlock the methods it needs to control the slider.

## Using the Slider component's exposed methods

How can we use the navigation methods that are provided by the `Slider` component? Well, it's actually quite simple, just like using a regular ref.

To start, we need to create a reference to the Slider component using `useRef()`, and then attach it to the `ref` attribute of the corresponding element. Here's how to do it:

```tsx
const sliderRef = React.useRef();

<Slider ref={sliderRef}>
    ...
</Slider>
```

Calling the Slider component methods is a breeze – simply access it through the `current` property of the ref.

```tsx
const handleClickNextButton = () => {
    const slider = sliderRef.current;
    if (slider) {
        slider.goToNextItem();
    }
};

<div className="container__next" onClick={handleClickNextButton}></div>
```

When the user clicks the Next button in this example, it triggers the `handleClickNextButton` function. This function retrieves the reference to the `Slider` component from `sliderRef.current`. If the reference exists, it calls the `goToNextItem()` method, which navigates to the next item in the slider.

Check out the final demo below! You can navigate between slider items by clicking either the arrows or the dots.

{% codesandbox 72yklj %}

## Conclusion

By using `useImperativeHandle`, we can create a well-defined and clean API for our component. This API allows users to control specific functionalities while still maintaining internal state and functionality. This way, we can ensure that the parent component doesn't accidentally modify any internal state or call any unnecessary functions.

In summary, `useImperativeHandle` gives us control over which methods or values we want to expose to the parent component. This makes our components more composable and easier to use in different contexts. It's a powerful tool that should be used with caution, but it can greatly improve the user experience of our applications.

---

It's highly recommended that you visit the [original post](https://phuoc.ng/collection/react-ref/expose-methods-of-a-component-using-use-imperative-handle/) to play with the interactive demos. 

If you found this series helpful, please consider giving the [repository](https://github.com/phuocng/master-of-react-ref) a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

If you want more helpful content like this, feel free to follow me:

-   [Twitter](https://twitter.com/_phuocng)
-   [GitHub](https://github.com/phuocng)