useImperativeHandle: the most unknown React hook
Tags: react, javascript, tutorial, beginners
Published: 2022-04-06 16:27:53

Among all the **native React hooks** that exist, there are well known ones like `useState`, `useEffect` and less known ones like `useImperativeHandle`.

This last hook is the subject of this article that I teased in my last article about [`React references`](https://dev.to/romaintrotard/things-you-need-to-know-about-react-ref-25ld).

At the end of this post, you will know what problem is solved with it, how to use it and a real use case where it's needed to be more concrete.

Buckle up, let's go.


## Introduction



In React, like in some other libraries, the data flow is unidirectional going top-down in the component tree. It means that a parent component can configure a child component thanks to **properties**. 
So in a sense, a child can have access to an element of the parent when passed as property. But a parent can't have access to an element of the child, or so you think.


![Unidirectional data flow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8z04y9uy2a9uv8n5bn3i.png)
 

> **Note:** We can inverse the data flow, by passing callback and value, for example when controlling components :)

If you want to expose a callback from the Child it can expose a `register` to register this `callback`:

```jsx
function Child({ register }) {
  useEffect(() => {
    const aFunction = () =>
      console.log("A function inside the FC");
    register(aFunction);
  }, [register]);

  return <p>Child</p>;
}

function Parent() {
  const childCallback = useRef();

  const register = useCallback((callback) => {
    // I use a ref but could be a state 
    // if needed to display JSX
    childCallback.current = callback;
  }, []);

  return <Child register={register} />;
}
```

Well, it works but it adds some boilerplate which is not the easiest to understand how it works.
It's time to go deep in the subject.

> **Warning:** You should not have to expose a state or variable from a child, in this case you would have to change the API and put the state up.

> **Note:** If you want to know more about **React state**, you can read my article [Things you need to know about React state](https://dev.to/romaintrotard/things-you-need-to-know-about-react-state-2l8o).

----

## Some context with Class components

Firstly, I would like to talk about the behavior of `ref` with **Class component**. 

> **Note:** If you want to know more about `ref` you can see my article [Things you need to know about React ref](https://dev.to/romaintrotard/things-you-need-to-know-about-react-ref-25ld).

When we pass a `ref` to a **Class component** then we get the **React element instance**.

```jsx
class ClassComponent extends React.Component {
  aFunction = () => console.log("A function inside the CC");

  render() {
    return <p>A class component</p>;
  }
}

function Parent() {
  const myRef = useRef();

  useEffect(() => {
    // Will log the React element instance
    console.log(myRef.current);
  }, []);

  return <ClassComponent ref={myRef} />;
}
```

It will then log:

![React element instance](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gg25lwp9ssvlq9jxm02x.png)


Thanks to that, you can call imperatively any method of a **Class component child** from its parent thanks to the `ref`.

```jsx
function Parent() {
  const myRef = useRef();

  return (
    <>
      <ClassComponent ref={myRef} />
      <button
        type="button"
        onClick={() => myRef.current.aFunction()}
      >
        Executes aFunction
      </button>
    </>
  );
}
```

> **Note:** Even if you have access to the `render` method, do not call it!

----

## Work with Functional components


If you try to do the same with a **Functional child component**, you will get the log `undefined`.


```jsx
function FunctionalComponent() {
  const aFunction = () =>
    console.log("A function inside the FC");

  return <p>A functional component</p>;
}

const ForwardedRefFunctionalComponent = React.forwardRef(
  FunctionalComponent
);

function Parent() {
  const myRef = useRef();

  useEffect(() => {
    // It will log `undefined`
    console.log(myRef.current);
  }, []);

  return <ForwardedRefFunctionalComponent ref={myRef} />;
}
```

> **Note:** We need to `forwardRef` with **Functional component** when customizing the reference named `ref`. But you can pass a reference as an usual prop and customize it too without the need of `forwardRef`.

```jsx
function FunctionalComponent({ aRef }) {
  const aFunction = () =>
    console.log("A function inside the FC");

  return <p>A functional component</p>;
}

function Parent() {
  const myRef = useRef();

  return <ForwardedRefFunctionalComponent aRef={myRef} />;
}
```

You probably have guessed it, `useImperativeHandle` will help you to solve it. The hook allows to expose some method of a child FC to its parent by customizing the passed reference.

----

## How to use it?

Now that we have the purpose of this hook, let's see how to use it.

It takes 3 parameters:
- the **reference to customize**
- the **API**s to expose as a callback
- an optional array of dependencies (when handler depends on state): has the same behavior than `useEffect` array dependency

> **Note:** Like `useEffect`, if you do not pass an array (as third argument), the API callback will be executed at each render.

```js
useImperativeHandle(ref, createHandle, [deps])
```

For example with the the previous example it will be:

```js
function FunctionalComponent(props, ref) {
  useImperativeHandle(ref, () => ({
    aFunction: () =>
      console.log("A function inside the FC"),
  }));

  return <p>A functional component</p>;
}
```

And now the log will be:

![useImperative console.log](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5s2du9atec3cddhdo11b.png)
 
Amazing, right? :)

> **Note:** You can also attach the `ref` to a DOM node (not possible with Class component) ;)

----

## A use case example

Well, now that you know how to use the hook, it's time to see a real use case where the component API is good and it's useful to use `useImperativeHandle`.

When displaying a list with a lot of elements, for example like Twitter, you can encounter some **layout performances** issues. This is where **virtualization** comes in handy. 
Basically, only the elements displayed on the screen are present in the DOM (with few element before and after) which makes the DOM much lighter.

To do that you would make a component named `VirtualizedList` which will handle virtualization. Behind the hood, there are some calculations in function of the height of rows and handling position to know which rows need to be in the DOM and where they are displayed on the screen.

We would like to be able to scroll to a specific items, in an imperative way. It's the perfect use case for `useImperativeHandle`:

```jsx
function VirtualizedList(props, ref) {
  useImperativeHandle(ref, () => ({
    scrollTo: (itemId) => {
      // Do some process
    },
  }));

  return (
    <ul>
      {
        // Right items to display
      }
    </ul>
  );
}
```

----

## Conclusion

Alright, now you have seen you can expose an API from a child to its parent, but do not overused it. Most of the time you will think you need it, it's probably that the API of your component is not right.
It's possible to do it in **Class components** and in **Functional components** as well. In FC, do not forget to `forwardRef` otherwise it would not work.

----

Do not hesitate to comment and if you want to see more, you can follow me on [Twitter](https://twitter.com/romain_trotard ) or go to my [Website](https://www.romaintrotard.com).