Hey visitors! If you're a beginner in WordPress and WP-CLI development, then you should check out my dear friend Bhargav's Youtube channel! @BuntyWP

Exposing multiple DOM elements to a parent Component using the useImperativeHandle hook

by Siddharth Thevaril

ReactJS provides a way to pass refs to a component using forwardRef. For example, an App that needs to access the DOM of it’s child component ( <IntroSection/> ), let’s say the <header/> HTML tag, you do something like:

import { forwardRef, useRef, useEffect } from 'react';

// App will access the <header /> tag using forwardRef
const IntroSection = forwardRef( ( props, ref ) => {
    return (
        <>
            <header ref={ ref }>A site header!</header>
        </>
    );
} );

// The main App.
function App() {
    const headerRef = useRef( null );

    useEffect( () => {
        headerRef.current.style.color = 'red';
    }, [] );

    return (
        <IntroSection ref={ headerRef } />
    );
}Code language: JavaScript (javascript)

Recently I came across a situation where the child Component had multiple HTML elements that I wanted to expose to the parent Component.

React does not have the provision to pass multiple multiple refs to a Component, however it provides a hook called useImperativeHandle to customise the handle exposed as a ref.

Note: useImperativeHandle is not just meant to expose multiple DOM elements. It does a lot of other cool things. This post limits its use to this specific use case.

Let’s modify the above example to expose an <article/> element.

import { forwardRef, useRef, useEffect, useImperativeHandle } from 'react';

// App will access the <header /> tag using forwardRef.
const IntroSection = forwardRef( ( props, ref ) => {
    const headerRef = useRef( null );
    const articleRef = useRef( null );

    useImperativeHandle( ref, () => {
        return {
            getHeaderRef() {
                return headerRef.current;
            },
            getArticleRef() {
                return articleRef.current;
            }
        }
    } );

    return (
      <>
            <header ref={ headerRef }>A site header!</header>
            <article ref={ articleRef }>The page article.</article>
      </>
    );
} );

// The main App.
function App() {
    const headerRef = useRef( null );

    useEffect( () => {
        headerRef.current.style.color = 'red';
    }, [] );

    return (
        <IntroSection ref={ headerRef } />
    );
}
Code language: JavaScript (javascript)

What did we do?

  • We created 2 refs for header and article inside the <IntroSection/> child component.
  • We used the useImperativeHandle() hook to customise the ref handle. Previously, the ref handle headerRef held a reference to the DOM element <header/>.
  • After customising it, headerRef will expose 2 methods: getHeaderRef() and getArticleRef() that each point to the <header/> and <article/> DOM elements.

Using the customised ref

The main part is done. What’s left is accessing the DOM elements. So we make some minor changes in the <App/> component.

// The main App.
function App() {
    const allRefs = useRef( null );

    useEffect( () => {
      allRefs.current.getHeaderRef().style.color = 'red';
      allRefs.current.getArticleRef().style.color = 'blue';
    }, [] );

    return (
        <IntroSection ref={ allRefs } />
    );
}
Code language: JavaScript (javascript)

For brevity, I have renamed headerRef to allRefs as the new ref will be used to access multiple DOM elements.

Like any other ref, the 2 new methods can be accessed on the current property of the allRefs ref.

That’s all folks! Hope you enjoyed reading this!

Subscribe to the Newsletter
Subscribe to get my latest content by email.

Leave a Reply