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

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!



Leave a Reply

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

The reCAPTCHA verification period has expired. Please reload the page.

Powered by WordPress