防止 eventListener 多次触发
Prevent eventListener from firing multiple times
我正在创建一个 HOC,如果输入组件有任何锚标记,它会添加从外部打开超链接的功能。
下面是 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 } />;
};
用法:
const CardWithExternalLinks = withExternalLink( Card );
如果 <CardWithExternalLinks />
在页面上使用一次,此方法有效。但是如果我们使用它 n
次,那么 useEffect()
运行 n
次,然后添加事件侦听器 n
次。
我能够通过将 openLinksExternally()
移到 withExternalLink()
之外来解决它,这样只有 1 个函数引用。但问题是我必须使用匿名函数来添加事件侦听器,因为我必须将 rootId
参数传递给 openLinksExternally()
.
因为我使用了匿名函数,所以每个 useEffect()
都会注册一个新的事件侦听器,每当单击 <a/>
标签时,openLinksExternally()
运行 n
次.
有没有更好的实现方法,让点击运行一次?
我用闭包解决了这个问题,这是我的解决方案:
export const withExternalLink = ( Component ) => ( rootId = false ) => {
let isListenerAttached = false;
return ( props ) => {
function openLinksExternally( e, id ) {
if ( ! id ) {
return;
}
const rootHtml = e.target.closest( id );
if ( ! rootHtml ) {
return;
}
const anchor = e.target.closest( 'a' );
if ( anchor ) {
anchor.setAttribute( 'target', '_blank' );
}
}
useEffect( () => {
if ( ! isListenerAttached ) {
document.addEventListener( 'click', ( e ) => openLinksExternally( e, rootId ) );
isListenerAttached = true;
return;
}
return () => document.removeEventListener( 'click', ( e ) => openLinksExternally( e, rootId ) );
}, [] );
return <Component { ...props } />;
};
};
我正在创建一个 HOC,如果输入组件有任何锚标记,它会添加从外部打开超链接的功能。
下面是 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 } />;
};
用法:
const CardWithExternalLinks = withExternalLink( Card );
如果 <CardWithExternalLinks />
在页面上使用一次,此方法有效。但是如果我们使用它 n
次,那么 useEffect()
运行 n
次,然后添加事件侦听器 n
次。
我能够通过将 openLinksExternally()
移到 withExternalLink()
之外来解决它,这样只有 1 个函数引用。但问题是我必须使用匿名函数来添加事件侦听器,因为我必须将 rootId
参数传递给 openLinksExternally()
.
因为我使用了匿名函数,所以每个 useEffect()
都会注册一个新的事件侦听器,每当单击 <a/>
标签时,openLinksExternally()
运行 n
次.
有没有更好的实现方法,让点击运行一次?
我用闭包解决了这个问题,这是我的解决方案:
export const withExternalLink = ( Component ) => ( rootId = false ) => {
let isListenerAttached = false;
return ( props ) => {
function openLinksExternally( e, id ) {
if ( ! id ) {
return;
}
const rootHtml = e.target.closest( id );
if ( ! rootHtml ) {
return;
}
const anchor = e.target.closest( 'a' );
if ( anchor ) {
anchor.setAttribute( 'target', '_blank' );
}
}
useEffect( () => {
if ( ! isListenerAttached ) {
document.addEventListener( 'click', ( e ) => openLinksExternally( e, rootId ) );
isListenerAttached = true;
return;
}
return () => document.removeEventListener( 'click', ( e ) => openLinksExternally( e, rootId ) );
}, [] );
return <Component { ...props } />;
};
};