如何在 React Hooks 中检测多个外部点击事件侦听器时提高性能



在搜索主题"检测外部单击React Hooks组件"后,我无法找出提高当前应用程序性能的解决方案。

  1. 上下文:我有多个 React 组件:

    • App:根组件,具有itemSelecting状态来检测当前项目的选择(FirstComponentSecondComponent,两个不同的组件(。它具有mousedown/mouseup事件侦听器,用于检测外部点击FirstComponentSecondComponent
import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import './styles.css';
import FirstComponent from './components/FirstComponent';
import SecondComponent from './components/SecondComponent';
import DropDownComponent from './components/DropdownComponent';
function App() {
const [itemSelecting, setItemSelecting] = useState(0);
function handleClickOutside(event) {
// FIXME: how to detect outside click 4 components
// console.log(event.target);
if (itemSelecting !== -1) setItemSelecting(0);
console.log('click first/second component');
}
useEffect(() => {
if (itemSelecting !== -1) {
document.addEventListener('mousedown', handleClickOutside);
} else {
document.removeEventListener('mousedown', handleClickOutside);
}
}, []);
const handleClick = value => {
if (value === itemSelecting) setItemSelecting(0);
else setItemSelecting(value);
};
return (
<React.Fragment>
<div className="App">
<FirstComponent
label="01"
selected={itemSelecting === 1}
handleClick={() => handleClick(1)}
/>
<SecondComponent
label="02"
selected={itemSelecting === 2}
handleClick={() => handleClick(2)}
/>
<FirstComponent
label="03"
selected={itemSelecting === 3}
handleClick={() => handleClick(3)}
/>
<SecondComponent
label="04"
selected={itemSelecting === 4}
handleClick={() => handleClick(4)}
/>
</div>
<hr />
<DropDownComponent />
</React.Fragment>
);
}
const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);
  • 第一组件:方形组件有selected道具经过App。可以选择(内部单击(/取消选择(外部单击或选中时再次单击(
import React from 'react';
import cn from 'classnames';
import styles from '../styles.module.css';
const FirstComponent = ({ label, selected, handleClick }) => {
const selectedClassName = selected ? styles.selected : '';
return (
<div
className={cn([styles.component, styles.first, selectedClassName])}
onClick={handleClick}>
<span>{label}</span>
</div>
);
};
export default FirstComponent;
  • 第二组件:圆形组件有App传递的selected道具。可以选择(内部单击(/取消选择(外部单击或选中时再次单击(
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;
  • 下拉列表组件:下拉列表组件可以展开(inside click(/折叠(outside click(。它有另一个mousedown/mouseup事件侦听器来检测外部点击DropDownComponent
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;
  1. 问题:同时,它有一些外部点击事件侦听器冗余。 示例:单击其中一个FirstComponent/SecondComponent,它出现:
outside click dropdown 
click first/second component
  1. 目标:如何一次检测多个组件上的外部点击事件侦听器? 我想优化:当单击FirstComponent/SecondComponent之一时,仅调用外部单击DropdownComponent

注意:实际上,我想在每个组件(FirstComponent/SecondComponent(中实现ref(useRef(来解决它,但我不知道如何(应用程序中的FIXME.js(

源代码演示在CodeSandbox上。

提前谢谢。

我认为您的问题是当您单击所选组件时,它们首先取消选择。这是因为 onClick 事件在释放鼠标按钮时触发,而外部单击在鼠标按下时触发。这可以通过在 addEventListener 中将鼠标向下替换为鼠标向上进行外部单击来修复。

另一个潜在的问题可能是,您没有从窗口中删除侦听器。您应该在 useEffect 钩子的返回函数中清除所有订阅。为此,最好将事件处理程序声明移动到 useEffect 中。由于外部单击事件处理程序使用 itemSelect 值,因此必须将其作为依赖项添加到 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]);

它是可选的,但我会从句柄单击外部处理程序中删除项目选择依赖项。所以UseEffect只调用了一次。这样它可能会更简单。

useEffect(() => {
const handleClickOutside = (event) => {
setItemSelecting(0);
console.log('click first/second component');
}
document.addEventListener('mouseup', handleClickOutside);
return () => document.removeEventListener('mouseup', handleClickOutside);
}, []);

我希望它有所帮助。 https://codesandbox.io/s/detect-multiple-outside-click-on-react-components-1hjg0

相关内容

  • 没有找到相关文章

最新更新