После поиска по теме " Обнаружение снаружи нажмите React Hooks component ", я не могу найти решение для улучшения производительности моего текущего приложения.
Контекст : У меня есть несколько компонентов React:
- Приложение : корневой компонент, имеет состояние
itemSelecting
для определения выбора текущего элемента (FirstComponent
или SecondComponent
, два разных компонента). Он имеет mousedown/mouseup
прослушиватель событий для обнаружения внешнего щелчка FirstComponent
или SecondComponent
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);
- FirstComponent: квадратный компонент имеет
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;
- SecondComponent : круговой компонент имеет
selected
пропусков, переданных App
. Он может быть выбран ( внутренний щелчок ) / не выбран ( внешний щелчок или щелкнуть снова, когда выбрано )
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;
- DropdownComponent : раскрывающийся компонент можно развернуть (
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;
Проблема : В то же время у него есть
внешний прослушиватель событий щелчка , избыточный . Пример: при нажатии на один из
FirstComponent
/
SecondComponent
появляется:
outside click dropdown
click first/second component
Цель : как я могу обнаружить
по одному разу внешний прослушиватель событий щелчка на нескольких компонентах? Я хочу оптимизировать: при нажатии на один из
FirstComponent
/
SecondComponent
вызывается только внешний щелчок
DropdownComponent
.
Примечание : На самом деле, я хотел быреализовать ref (useRef
) внутри каждого компонента (FirstComponent
/ SecondComponent
), чтобы разрешить его, но я не знаю, как (FIXMEв App.js)
Исходный код демо включен CodeSandbox .
Заранее спасибо.