I am creating an HOC which adds the functionality to open hyperlinks externally if the input Component has any anchor tags.
Below is the HOC:
export const withExternalLink = ( Component, rootId = false ) => ( props ) => {
function openLinksExternally( e, rootId ) {
const rootHtml = e.target.closest( rootId );
if ( ! rootHtml ) {
return;
}
const anchor = e.target.closest( 'a' );
if ( anchor ) {
e.preventDefault();
/**
* Logic to open link externally.
*/
}
}
useEffect( () => {
document.addEventListener( 'click', ( e ) => openLinksExternally( e, rootId ) );
return () => document.removeEventListener( 'click', ( e ) => openLinksExternally( e, rootId ) );
}, [] );
return <Component { ...props } />;
};
Usage:
const CardWithExternalLinks = withExternalLink( Card );
This works if the <CardWithExternalLinks /> is used once on a page. But if we use it n times, then useEffect() runs n times, which then adds event listener n times.
I was able to solve it by moving openLinksExternally() outside of withExternalLink(), this way there is just 1 reference to the function. But the problem is that I have to use an anonymous function to add event listener because I have to pass the rootId parameter to openLinksExternally().
Since I have used anonymous functions, a new event listener is registered for every useEffect(), and whenever an <a/> tag is clicked, openLinksExternally() runs n times.
Is there a way to better implement this so that the click runs once?