将 HTML 元素的 ref 传递给自定义钩子

IT技术 javascript reactjs react-hooks
2021-05-18 19:36:58

想象一下,我有一个自定义钩子,我将使用它向 HTML 元素中添加单击事件侦听器。

我用 来创建 ref const buttonRef = useRef(null);,所以第一次渲染时的值为空。ref 值仅在渲染方法的最后阶段分配,在我的自定义钩子已经被调用的地方。

因此,在第一次渲染时,我的自定义钩子没有任何可以添加事件侦听器的内容。

我最终不得不在我的自定义钩子第二次运行之前更新组件,并最终将事件侦听器添加到元素。我得到以下行为:

在此处输入图片说明

沙盒示例

问题:

如何解决这个问题?我真的需要强制更新我的组件才能向自定义钩子发送引用吗?因为我只能在顶层调用钩子和自定义钩子(钩子规则),所以不允许像下面这样。

useEffect(() => {
  useMyHook();
});

应用程序.js

function App() {
  const buttonRef = useRef(null);
  const hookValue = useMyHook(buttonRef.current);
  const [forceUpdate, setForceUpdate] = useState(false);

  return (
    <div>
      <button onClick={() => setForceUpdate(prevState => !prevState)}>
        Update Component
      </button>
      <button ref={buttonRef}>Update Hook</button>
      {"This is hook returned value: " + hookValue}
    </div>
  );
}

useMyHook.js(自定义钩子)

import { useEffect, useState } from "react";

function useMyHook(element) {
  const [myHookState, setMyHookState] = useState(0);

  console.log("Inside useMyhook...");
  console.log("This is the element received: " + element);

  useEffect(() => {
    console.log("Inside useMyhook useEffect...");

    function onClick() {
      setMyHookState(prevState => prevState + 1);
    }

    if (element !== null) {
      element.addEventListener("click", onClick);
    }
    return () => {
      console.log("Inside useMyhook useEffect return...");
      if (element !== null) {
        element.removeEventListener("click", onClick);
      }
    };
  });

  return myHookState;
}

export default useMyHook;
1个回答

解决方案非常简单,您只需要将 ref 传递给自定义钩子,而不是ref.current因为,当将 ref 分配给 DOM 并且自定义钩子中的 useEffect 被触发后,ref.current 在其原始引用处发生变异第一次渲染,因此buttonRef.current将引用 DOM 节点

使用MyHook.js

import { useEffect, useState } from "react";

function useMyHook(refEl) {
  const [myHookState, setMyHookState] = useState(0);

  console.log("Inside useMyhook...");
  console.log("This is the element received: ", refEl);

  useEffect(() => {
    const element = refEl.current;
    console.log("Inside useMyhook useEffect...");

    function onClick() {
      console.log("click");
      setMyHookState(prevState => prevState + 1);
    }
    console.log(element);
    if (element !== null) {
      element.addEventListener("click", onClick);
    }
    return () => {
      console.log("Inside useMyhook useEffect return...");
      if (element !== null) {
        element.removeEventListener("click", onClick);
      }
    };
  }, []);

  return myHookState;
}

export default useMyHook;

索引.js

function App() {
  const buttonRef = useRef(null);
  const hookValue = useMyHook(buttonRef);
  const [forceUpdate, setForceUpdate] = useState(false);

  return (
    <div>
      <button onClick={() => setForceUpdate(prevState => !prevState)}>
        Update Component
      </button>
      <button ref={buttonRef}>Update Hook</button>
      {"This is hook returned value: " + hookValue}
    </div>
  );
}

工作演示