将 HTML 元素的 ref 传递给自定义钩子 [英] Passing a ref to an HTML element to a custom hook

查看:65
本文介绍了将 HTML 元素的 ref 传递给自定义钩子的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

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

沙盒示例

问题:

如何解决这个问题?我真的需要强制更新我的组件才能向自定义钩子发送引用吗?由于我只能在顶层调用钩子和自定义钩子(钩子规则),因此不允许使用以下内容.

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

<小时>

App.js

function App() {const buttonRef = useRef(null);const hookValue = useMyHook(buttonRef.current);const [forceUpdate, setForceUpdate] = useState(false);返回 (<div><button onClick={() =>setForceUpdate(prevState => !prevState)}>更新组件<button ref={buttonRef}>更新钩子</button>{"这是钩子返回值:" + hookValue}

);}

useMyHook.js(自定义钩子)

import { useEffect, useState } from "react";函数 useMyHook(element) {const [myHookState, setMyHookState] = useState(0);console.log("useMyhook 内部...");console.log("这是收到的元素:" + element);useEffect(() => {console.log("useMyhook useEffect 里面...");函数 onClick() {setMyHookState(prevState => prevState + 1);}如果(元素!== 空){element.addEventListener("click", onClick);}返回 () =>{console.log("useMyhook useEffect 内部返回...");如果(元素!== 空){element.removeEventListener("click", onClick);}};});返回 myHookState;}导出默认 useMyHook;

解决方案

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

useMyHook.js

import { useEffect, useState } from "react";函数 useMyHook(refEl) {const [myHookState, setMyHookState] = useState(0);console.log("useMyhook 内部...");console.log("这是接收到的元素:", refEl);useEffect(() => {const 元素 = refEl.current;console.log("useMyhook useEffect 里面...");函数 onClick() {console.log("点击");setMyHookState(prevState => prevState + 1);}控制台日志(元素);如果(元素!== 空){element.addEventListener("click", onClick);}返回 () =>{console.log("useMyhook useEffect 内部返回...");如果(元素!== 空){element.removeEventListener("click", onClick);}};}, []);返回 myHookState;}导出默认 useMyHook;

<小时>

index.js

function App() {const buttonRef = useRef(null);const hookValue = useMyHook(buttonRef);const [forceUpdate, setForceUpdate] = useState(false);返回 (<div><button onClick={() =>setForceUpdate(prevState => !prevState)}>更新组件<button ref={buttonRef}>更新钩子</button>{"这是钩子返回值:" + hookValue}

);}

工作演示

Imagine I have a custom hook that I'll use to add a click event listener into an HTML element.

I create the ref with const buttonRef = useRef(null); , so the value on first render is null. The ref value is only assigned in the final of the render method, at a point where my custom hook has already been called.

Therefore, on first render, my custom hook doesn't have anything to add an event listener to.

I end up having to update the component before my custom hook can run for the second time and finally add the event listener to the element. And I get the following behavior:

Sandbox with example

QUESTION:

How to get around this? Do I really need to force update my component in order to send a ref to a custom hook? Since I only can call hooks and custom hooks at top-level (rules of hooks), something like the following it's not allowed.

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


App.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 (custom hook)

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;

解决方案

The solution is pretty trivial, you just need to pass on the ref to the custom hook instead of ref.current since, ref.current is mutated at its original reference when the ref is assigned to the DOM and the useEffect in your custom hook is triggered post the first render, so buttonRef.current will reference the DOM node

useMyHook.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;


index.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>
  );
}

Working demo

这篇关于将 HTML 元素的 ref 传递给自定义钩子的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆