仅在 dom 中的活动组件上调用自定义钩子

问题描述 投票:0回答:1

首先,抱歉我的标题不好。

我的目标是使用此自定义挂钩检测组件外部的点击:

  const useOutsideClick = (callback) => {
    const reference = useRef();

    useEffect(() => {
      const handleClick = (event) => {
        if (reference.current && !reference.current.contains(event.target)) {
          console.log(reference.current);
          console.log(event.target);

          callback();
        }
      };

      document.addEventListener("click", handleClick);

      return () => {
        document.removeEventListener("click", handleClick);
      };
    }, [reference]);

    return reference;
  };

我有一个

<HeaderSearch />
自定义组件,如下所示:

  const HeaderSearch = ({ screenWidth }) => {
   
    function checkInput(event) {
      if (event.keyCode !== 13) {
        setSearch(event.target.value);
      }
    }
    const mobileRef = useOutsideClick(test);
    const ref = useOutsideClick(test);

    if (location.pathname !== "/") {
      if (screenWidth == "mobile") {

        return (
          <div id="search-header" className="search-header mob" ref={mobileRef}>
            <FontAwesomeIcon
              className="search-icon"
              id="search-icon"
              icon={faSearch}
              onClick={() => toggleSearchInput()}
            />
            <input
              id="search-input"
              className={searchActive ? "search-input active" : "search-input"}
              type="text"
              value={search}
              onKeyDown={(e) => checkInput(e)}
              onChange={(e) => setSearch(e.target.value)}
              // ref={searchInputRef}
              // onClick={() => searchReqClientSide()}
              placeholder="Search..."
            />
            <button></button>
          </div>
        );
      } else {

        return (
          <div id="search-header" className="search-header desk" ref={ref}>
            <FontAwesomeIcon
              className="search-icon"
              id="search-icon"
              icon={faSearch}
              onClick={() => toggleSearchInput()}
            />
            <input
              id="search-input"
              className={searchActive ? "search-input active" : "search-input"}
              type="text"
              value={search}
              onKeyDown={(e) => checkInput(e)}
              onChange={(e) => setSearch(e.target.value)}
              // ref={searchInputRef}
              // onClick={() => searchReqClientSide()}
              placeholder="Search..."
            />
            <button></button>
          </div>
        );
      }
    } else {
      return null;
    }
  };

由于 css 类,HeaderSearch 组件是有条件渲染的。如果父标题元素具有活动类:将没有活动的标题的显示更改为不显示。

但是,当我单击组件外部时。两个 HeaderSearch 组件仍在调用自定义挂钩。请参阅下面的屏幕截图: enter image description here

如何才能使只有处于活动状态的

<HeaderSearch/>
才调用 useOutsideClick 挂钩,而不是每次点击都触发?

编辑:我忘记添加 Test 回调(它是一个简单的日志):

function test() {
    console.log("clicked outside");
  }
javascript reactjs react-hooks
1个回答
0
投票

重构逻辑,只需要一个

useOutsideClick
钩子。由于标题内容(即子项)对于移动设备和桌面视图来说似乎是相同的,因此您可以创建一个包装器组件来替换包装
div
元素,有条件地应用正确的 CSS
className
prop、 和任何其他布局/移动和桌面视图之间的 UI 差异

实施示例:

const SearchHeaderWrapper = ({
  children,
  onClickOutside,
  screenWidth
}) => {
  const ref = useOutsideClick(onClickOutside);

  return (
    <div
      ref={ref}
      id="search-header"
      className={[
        "search-header",
        screenWidth == "mobile" ? "mob" : "desk"
      ].join(" ")}
      ref={ref}
    >
      {children}
    </div>
  );
};
const HeaderSearch = ({ screenWidth }) => {
  const ref = useOutsideClick(test);
  ...

  function checkInput(event) {
    if (event.keyCode !== 13) {
      setSearch(event.target.value);
    }
  }

  if (location.pathname !== "/") {
    return (
      <SearchHeaderWrapper
        onClickOutside={test}
        screenWidth={screenWidth}
      >
        <FontAwesomeIcon
          className="search-icon"
          id="search-icon"
          icon={faSearch}
          onClick={() => toggleSearchInput()}
        />
        <input
          id="search-input"
          className={["search-input", searchActive && "active"]
            .filter(Boolean)
            .join(" ")
          }
          type="text"
          value={search}
          onKeyDown={(e) => checkInput(e)}
          onChange={(e) => setSearch(e.target.value)}
          // ref={searchInputRef}
          // onClick={() => searchReqClientSide()}
          placeholder="Search..."
        />
        <button></button>
      </div>
    );
  } else {
    return null;
  }
};
© www.soinside.com 2019 - 2024. All rights reserved.