问题描述
我想使用自定义钩子切换输入元素。
这是我的自定义钩子:
import { RefObject,useEffect } from "react";
export const useEscape = (
ref: RefObject<HTMLElement>,triggerFn: () => void
) => {
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
triggerFn();
}
};
document.addEventListener("click",handleClickOutside);
return () => window.removeEventListener("click",handleClickOutside);
});
};
以及使用钩子的示例
import * as React from "react";
import "./styles.css";
import { useEscape } from "./useEscape";
export default function App() {
const [showInput,setShowInput] = React.useState(false);
const inputRef = React.useRef(null);
useEscape(inputRef,() => {
if (showInput) setShowInput(false);
});
return (
<div>
{showInput && (
<input ref={inputRef} placeholder="click outside to toggle" />
)}
{!showInput && (
<span
style={{ border: "1px solid black" }}
onClick={() => {
console.log("toggle to trigger");
setShowInput(true);
}}
>
click to toggle input
</span>
)}
</div>
);
}
问题来了。在我点击 span
元素切换到输入状态后。在 input 元素外单击后,它将永远无法再次切换回 input 状态。
我想我知道为什么会这样。 react ref 仍然指向最初创建的 input
元素。然而,当反应切换到显示 span
状态时,它会卸载 input
元素,并且我的自定义钩子永远不会与新的 input
元素的 React 同步。如何自定义我的 useEscape
钩子,以便 react ref 同步? (顺便说一下,我不想使用样式作为在视觉上“隐藏”输入元素的解决方法)。
解决方法
import { RefObject,useEffect } from "react";
export const useEscape = (
ref: RefObject<HTMLElement>,triggerFn: () => void
) => {
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (ref.current && !ref.current.contains(event.target as Node)) {
triggerFn();
}
};
document.addEventListener("click",handleClickOutside);
return () => document.removeEventListener("click",handleClickOutside);
},[ref,triggerFn]);
};
你的整个逻辑是绝对正确的。有一个轻微的错误,而不是 window.removeEventListener,改成document.removeEventListener。
您正在删除导致错误的全局窗口对象上的事件侦听器。