两个侦听器都连接到冒泡阶段,因此内部侦听器首先触发.
当项目显示并单击时,将运行:
<div
className='item-content'
onClick={() => setDisplayItem(false)}
>item content</div>
因此,在事件向外传播之前,setDisplayItem(false)
会导致这个.item-content
元素从DOM中删除.看这里,父母如何在之后不再存在:
const Container = ({ setDisplay }) => {
const containerRef = React.useRef(null);
useClickOutside(containerRef, () => {
//code to make Container disappear that is not relevant for the issue
console.log('making container disappear');
});
return (
<div ref={containerRef} className='container'>
container
<Item />
</div>
);
};
const Item = () => {
const [displayItem, setDisplayItem] = React.useState(false);
return (
<div>
<button onClick={() => setDisplayItem(true)}>Show Item's content</button>
{displayItem && (
<div
className='item-content'
onClick={() => setDisplayItem(false)}
>item content</div>
)}
</div>
);
};
const useClickOutside = (ref, handler) => {
React.useEffect(() => {
const trigger = e => {
console.log(e.target.parentElement);
if (!(ref.current.contains(e.target))) handler();
}
document.addEventListener('click', trigger);
return () => document.removeEventListener('click', trigger);
}, [handler, ref])
}
ReactDOM.render(<Container />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>
您可以通过更改useClickOutside
来修复此问题,同时判断 node 是否已连接.如果未连接,则由于状态更改和重新渲染,元素不再位于DOM中-因此所做的单击不是ref.current
之外的definitely,因此处理程序不应运行.
const trigger = e => {
const { current } = ref;
if (e.target.isConnected && !current.contains(e.target)) {
const Container = ({ setDisplay }) => {
const containerRef = React.useRef(null);
useClickOutside(containerRef, () => {
//code to make Container disappear that is not relevant for the issue
console.log('making container disappear');
});
return (
<div ref={containerRef} className='container'>
container
<Item />
</div>
);
};
const Item = () => {
const [displayItem, setDisplayItem] = React.useState(false);
return (
<div>
<button onClick={() => setDisplayItem(true)}>Show Item's content</button>
{displayItem && (
<div
className='item-content'
onClick={() => setDisplayItem(false)}
>item content</div>
)}
</div>
);
};
const useClickOutside = (ref, handler) => {
React.useEffect(() => {
const trigger = e => {
const { current } = ref;
if (e.target.isConnected && !current.contains(e.target)) {
console.log(current.parentElement);
handler();
}
}
document.addEventListener('click', trigger);
return () => document.removeEventListener('click', trigger);
}, [handler, ref])
}
ReactDOM.render(<Container />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>