簡體   English   中英

在設置處理程序之前調用事件處理程序 get

[英]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 渲染組件時調用useEffectwindow.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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM