onClick事件.detail包含点击次数:双击有
event.detail = 2
。
当点击的React组件被替换时,event.detail不会重置。我怀疑这是因为 React 对此进行了优化并在页面中保留了相同的 div,因此浏览器没有重置 event.detail 计数器。
如何在组件更改时强制重置此 event.detail 计数器?我希望能够在导航时多次双击列表中的项目。
问题重现:https://codesandbox.io/p/sandbox/react-on-click-event-detail-6ndl5v?file=%2Fsrc%2FApp.tsx%3A44%2C12
App.tsx:
const ListItem: React.FC<{
content: string;
onClick: (event: React.MouseEvent<HTMLDivElement>) => void;
}> = ({ content, onClick }) => {
return (
<div
onClick={onClick}
style={{ background: "rgba(0,0,0,0.5)", userSelect: "none" }}
>
{content}
</div>
);
};
const ListRenderer: React.FC = () => {
// n tracks the number of double clicks
const [n, setN] = useState(0);
// items simulates an updating list of items when double-clicking
const items = useMemo(
() => Array.from({ length: 10 }, (_, i) => `${n}: item ${i}`),
[n]
);
const handleClick = useCallback((event: React.MouseEvent<HTMLDivElement>) => {
console.log(`Click: event.detail: ${event.detail}`);
if (event.detail === 2) {
// Double-clicked an item.
setN((prev) => prev + 1);
}
}, []);
return (
<>
<div>
{items.map((item, index) => (
<ListItem
key={`items-${n}-${index}`}
content={item}
onClick={handleClick}
/>
))}
</div>
</>
);
};
经过一些实验,我编写了这个 React Hook 来检测重新安装组件后 event.detail 何时未正确重置,并相应地更正点击计数:
import { useCallback, useRef } from 'react'
// MouseEvent and other events satisfy this.
interface DetailCountEvent {
// detail is the number of times the event occurred.
detail: number
}
// useDetailCountHandler builds an event handler which correctly resets the
// event.detail counter when the component is re-mounted.
//
// The onClick event.detail contains the number of clicks: double-click has
// event.detail = 2. When the clicked React component is replaced, the
// event.detail does not reset.
//
// Question: https://stackoverflow.com/q/77719428/431369
// Issue: https://codesandbox.io/p/sandbox/react-on-click-event-detail-6ndl5v?file=%2Fsrc%2FApp.tsx%3A8%2C23
// Fix: https://codesandbox.io/p/sandbox/react-on-click-event-detail-possible-fix-4zwk7d?file=%2Fsrc%2FApp.tsx%3A59%2C1
export function useDetailCountHandler<E extends DetailCountEvent>(
cb: (e: E, count: number) => void,
) {
const stateRef = useRef({ prev: 0, sub: 0 })
return useCallback(
(e: E) => {
const state = stateRef.current
let count = e.detail
if (state.sub >= count) {
state.sub = 0
}
if (state.prev < count - 1) {
state.sub = count - state.prev - 1
}
count -= state.sub
state.prev = count
cb(e, count)
},
[stateRef, cb],
)
}
用途:
const handleClick: MouseEventHandler<HTMLDivElement> =
useDetailCountHandler(
useCallback(
(e, count) => {
console.log('onClick', e.detail, count);
if (count === 2) {
// double click
}
},
[],
),
);
return <div onClick={handleClick} />;
使用
key
强制React重新创建div,不幸的是不会重置onClick中的event.detail。
上面的钩子有点像黑客,但工作可靠。如果其他人有一种在组件更改时重置 event.detail 的不那么麻烦的方法,我很想知道。