useImperativeHandle: A New Perspective in Making React Components
Tags: react, hook
Published: 2022-06-11 07:36:08

`useImperativeHandle` hook is an unusual hook in React, to an extent that it is discouraged to use in the React's official documentation. React is a declarative framework and as the hook's name suggests, it is giving a break from React's declarative style by letting parent component call functions within the child component.

This is not something new to React, we have the `useRef`/`createRef` to access a DOM Elements API and call their functions. But in the case of a child component, you can't access functions within it and call them to make changes in it using `useRef`. 

Let's look at some code.

```jsx
const App = () => {
  const [isVisible, setVisible] = useState(false);

  const showDialog = () => setVisible(true);

  return (
    <>
      <button onClick={showDialog}>
        Show Dialog
      </button>
      <Dialog isVisible={isVisible}>
        Content within dialog
      </Dialog>
    </>
  )
} 
```

This is the declarative way of implementing a dialog component. You have to pass visibility as a property to the component. This is the default way in which we all work.

Let's look at an alternative but imperative way.

```jsx
const App = () => {
  const dialogRef = useRef();

  return (
    <>
      <button onClick={() => dialogRef.current.show()}>
        Show Dialog
      </button>
      <Dialog isVisible={isVisible}>
        Content within dialog
      </Dialog>
    </>
  )
}
```

Here we are accessing the component as if it's an object and call functions on it. State of the component is maitained within itself. We don't see such code often in React projects but it's certainly possible using `useImperativeHandle`.

Following is the implementation of such a `Dialog` component.

```jsx
const Dialog = React.forwardRef((props, ref) => {
  
  const [visible, setVisible] = useState(false);

  useImperativeHandle(ref, () => {
    return {
      show() {
        setVisible(true);
      },
      hide() {
        setVisible(false);
      },
      toggle() {
        setVisible(v => !v);
      }
    }
  } );

  return (
    <div class="dialog-style">
      This is a dialog box
    </div>
  );
});
```

We have to use `forwardRef` to take the ref from parent and assign it to the object that is returned from the second argument of `useImperativeHandle`. That's it. Now we have exposed our component with a custom API to the parent component.

State of the dialog's visibility is kept within the component and this relieves us from defining the state everytime we have to use this component. But this doesn't mean we should use `useImperativeHandle` more often. 

I chose Dialog component as an example, because showing dialogs is one of the cases which happen mostly on an event like button click. So imperative API may be easier to consume in this case. But React is designed as a declarative framework and popular packages like Redux maintain the state and the UI is just a representation of state maintained somewhere else. So for most cases, if you use imperative API, you have to use a lot of `useEffect`s to trigger these imperative APIs which is not good.

There are some cases where it suits better. Whenever you want expose functions from more than one DOM Element to a parent component, you can go for `useImperativeHandle`.

Imagine that you build a credit card input component where you have four input boxes and at times first or last input has to be focussed. In such a case, `useImperativeHandle` is a good choice. Check out the code below.

```jsx
const CreditCardInput = React.forwardRef((props, ref) => {

  const firstInputRef = useRef();
  const lastInputRef = useRef();

  useImperativeHandle(ref, () => {
    return {
      focusFirst() {
        firstInputRef.current.focus()
      },
      focusSecond() {
        secondInputRef.current.focus()
      },
    }
  });

  return (
    <div class="style">
      <input type="number" ref={firstRef} />
      <input type="number" />
      <input type="number" />
      <input type="number" ref={lasttRef} />
    </div>
  );
});
```

`useImperativeHandle` is a nice tool which has to be rarely and cautiously used. Otherwise you could easily end up inventing *Angular* in *React*. Think about the use case you have in hand before coming to a conclusion.