Javascript 如何在React钩子中检测多个外部单击事件侦听器时提高性能
在搜索主题“”后,我无法找到提高当前应用程序性能的解决方案Javascript 如何在React钩子中检测多个外部单击事件侦听器时提高性能,javascript,reactjs,performance,react-hooks,Javascript,Reactjs,Performance,React Hooks,在搜索主题“”后,我无法找到提高当前应用程序性能的解决方案 上下文:我有多个组件: 应用程序:根组件具有项目选择状态以检测当前项目选择(第一个组件或第二个组件,两个不同的组件)。它有一个mousedown/mouseup事件侦听器来检测外部单击FirstComponent或SecondComponent SecondComponent:圆圈组件选择了道具,道具经过App。可以选择(内部单击)/取消选择(外部单击或选择后再次单击) 问题:同时,它有一些外部点击事件监听器冗余。 示例:单击F
- 应用程序:根组件具有
状态以检测当前项目选择(项目选择
或第一个组件
,两个不同的组件)。它有一个第二个组件
事件侦听器来检测外部单击mousedown/mouseup
或FirstComponent
SecondComponent
- SecondComponent:圆圈组件选择了
道具,道具经过
。可以选择(内部单击)/取消选择(外部单击或选择后再次单击)App
FirstComponent
/SecondComponent
中的一个组件时,会出现:FirstComponent
/SecondComponent
中的一个时,只调用外部的单击DropdownComponent
FirstComponent
/SecondComponent
)中实现ref(useRef
)来解决它,但我不知道如何解决(在App.js中修复我)
源代码演示已打开
提前谢谢 我认为您的问题在于,当您单击所选组件时,会首先取消选择它们。这是因为onClick事件在释放鼠标按钮时触发,而外部单击在鼠标按下时触发。这可以通过在addEventListener中用mouseup替换mousedown以进行外部单击来解决
另一个潜在的问题可能是,您没有从窗口中删除侦听器。您应该在useEffect挂钩的返回函数中清除所有订阅。为此,最好将事件处理程序声明移动到useEffect中。由于外部单击事件处理程序使用itemSelecting值,因此必须将其作为依赖项添加到useEffect
useEffect(() => {
const handleClickOutside = (event) => {
if (itemSelecting !== -1) setItemSelecting(0);
console.log('click first/second component');
}
document.addEventListener('mouseup', handleClickOutside);
return () => document.removeEventListener('mouseup', handleClickOutside);
}, [itemSelecting]);
它是可选的,但我将从handleClickOutside处理程序中删除itemSelecting依赖项。所以useffect只调用一次。这样可以更简单
useEffect(() => {
const handleClickOutside = (event) => {
setItemSelecting(0);
console.log('click first/second component');
}
document.addEventListener('mouseup', handleClickOutside);
return () => document.removeEventListener('mouseup', handleClickOutside);
}, []);
我希望有帮助。
mouseup解决方案非常适合我,非常感谢!
import React from 'react';
import cn from 'classnames';
import styles from '../styles.module.css';
const SecondComponent = ({ label, selected, handleClick }) => {
const selectedClassName = selected ? styles.selected : '';
return (
<div
className={cn([styles.component, styles.second, selectedClassName])}
onClick={handleClick}>
<span>{label}</span>
</div>
);
};
export default SecondComponent;
import React, { useState, useRef, useEffect } from 'react';
import PropTypes from 'prop-types';
import styles from '../styles.module.css';
const DropDownComponent = () => {
const nodeRef = useRef(null);
const listRef = useRef(null);
const [isOpen, setIsOpen] = useState(false);
const handleClick = event => {
if (nodeRef && nodeRef.current.contains(event.target)) {
// inside click
if (
listRef &&
listRef.current &&
listRef.current.contains(event.target)
) {
setTimeout(() => {
setIsOpen(false);
}, 500);
}
return;
}
setIsOpen(false);
console.log('outside click dropdown');
};
useEffect(() => {
document.addEventListener('mousedown', handleClick);
return () => document.removeEventListener('mousedown', handleClick);
}, []);
return (
<div ref={nodeRef} className={styles.dropdown}>
<button className={styles.dropbtn} onClick={() => setIsOpen(!isOpen)}>
Dropdown
</button>
{isOpen && (
<div ref={listRef} className={styles.dropdownContent}>
<a>Link 1</a>
<a>Link 2</a>
<a>Link 3</a>
</div>
)}
</div>
);
};
DropDownComponent.defaultProps = {
classNameType: null,
isOpen: false,
handleIsOpen: () => {},
title: null,
};
DropDownComponent.propTypes = {
classNameType: PropTypes.string,
isOpen: PropTypes.bool,
handleIsOpen: PropTypes.func,
title: PropTypes.string,
};
export default DropDownComponent;
outside click dropdown
click first/second component
useEffect(() => {
const handleClickOutside = (event) => {
if (itemSelecting !== -1) setItemSelecting(0);
console.log('click first/second component');
}
document.addEventListener('mouseup', handleClickOutside);
return () => document.removeEventListener('mouseup', handleClickOutside);
}, [itemSelecting]);
useEffect(() => {
const handleClickOutside = (event) => {
setItemSelecting(0);
console.log('click first/second component');
}
document.addEventListener('mouseup', handleClickOutside);
return () => document.removeEventListener('mouseup', handleClickOutside);
}, []);