[英]Event handler get's called before handler is set
我的 React 應用程序中有這個可重用的組件。
export const OutsideWrapper = ({ children, onOutside, className }) => {
const wrapperRef = useRef(null);
const [style, setStyles] = useState({
opacity: 1
});
useEffect(() => {
console.log("1. component was mounted");
const i = e => {
if (wrapperRef.current && !wrapperRef.current.contains(e.target)) {
console.log("3. outside click was trigerred");
e.preventDefault();
setStyles({ opacity: 0 });
setTimeout(() => {
onOutside();
}, 100);
}
};
window.addEventListener("click", i, true);
console.log('2. listener was added');
return () => {
console.log("4. listerner was removed");
window.removeEventListener("click", i, true);
};
}, [onOutside]);
return (
<div
ref={wrapperRef}
style={style}
className={`outside-wrapper ${className}`}
>
{children}
</div>
);
};
當這個OutsideWrapper
組件被渲染時,它應該向文檔添加事件監聽器,然后監聽一個事件,調用onOutside
函數然后卸載。 ( onOutside
卸載組件)。 在該偵聽器被刪除之后。
但是當組件被渲染時,它會立即調用onOutside
並卸載。
這是父組件的部分:
const [down, setDown] = useState(false);
return (
<input onFocus={()=>setDown(true)}/>
{down && (
<OutsideWrapper
onOutside={() => setDown(false)}
className="input-wrapper"
>
<DropDownList
items={dropDownItems}
term={data.location}
onChoose={onChoose}
/>
</OutsideWrapper>
)}
)
當 React 渲染組件時調用useEffect
的window.addEventListener
調用,這發生在focus 上。 引起焦點的事件不是點擊,而是mousedown
。 當下一次鼠標mouseup
時,還會生成一個單擊事件並被捕獲。 請注意,如果您按 Tab 鍵進入輸入以聚焦,則不會導致錯誤。
有幾種方法可以解決這個問題,但我的建議是忽略輸入本身發生的點擊事件。
這是一個例子:我已經向<input>
添加了一個 ref ,將它傳遞給OutsideWrapper
,並添加了一個檢查,就像你對新 ref 的wrapperRef
。
function Test() {
const [down, setDown] = React.useState(false);
const focusRef = React.useRef();
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>
<input ref={focusRef} onFocus={() => setDown(true)} />
{down && (
<OutsideWrapper
onOutside={() => setDown(false)}
focusedRef={focusRef}
className="input-wrapper"
>
children
</OutsideWrapper>
)}
</h2>
</div>
);
}
const OutsideWrapper = ({ children, onOutside, className, focusedRef }) => {
const wrapperRef = React.useRef(null);
const [style, setStyles] = React.useState({
opacity: 1
});
React.useEffect(() => {
console.log("1. component was mounted");
const i = e => {
console.log(e.target, wrapperRef.current);
if (
wrapperRef.current &&
!wrapperRef.current.contains(e.target) &&
focusedRef.current !== e.target
) {
console.log("3. outside click was trigerred");
e.preventDefault();
setStyles({ opacity: 0 });
setTimeout(() => {
onOutside();
}, 100);
}
};
window.addEventListener("click", i, true);
console.log("2. listener was added");
return () => {
console.log("4. listerner was removed");
window.removeEventListener("click", i, true);
};
}, [onOutside, focusedRef]);
return (
<div
ref={wrapperRef}
style={style}
className={`outside-wrapper ${className}`}
>
{children}
</div>
);
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.