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 handleheaderRef
held a reference to the DOM element<header/>
. - After customising it,
headerRef
will expose 2 methods:getHeaderRef()
andgetArticleRef()
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