Передача ссылки на элемент HTML в пользовательский хук - PullRequest
0 голосов
/ 14 мая 2019

Представьте, что у меня есть пользовательский хук, который я буду использовать для добавления прослушивателя события click в элемент HTML.

Я создаю ссылку с const buttonRef = useRef(null);, поэтому значение при первом рендере равно нулю.Значение ref присваивается только в финале метода рендеринга, в точке, где мой пользовательский хук уже был вызван.

Следовательно, при первом рендеринге мой пользовательский хук не имеет ничего для добавления прослушивателя событий.

В конечном итоге мне приходится обновлять компонент, прежде чем мой пользовательский хук может работать во второмвремя и, наконец, добавить слушатель события к элементу.И я получаю следующее поведение:

enter image description here

Песочница с примером

ВОПРОС:

Как это обойти?Мне действительно нужно принудительно обновить мой компонент, чтобы отправить ссылку на пользовательский хук?Поскольку я могу вызывать только хуки и пользовательские хуки на верхнем уровне (правила хуков), что-то вроде следующего недопустимо.

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 (настраиваемый хук)

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 Ответ

1 голос
/ 14 мая 2019

Решение довольно тривиально, вам просто нужно передать ссылку на пользовательский хук вместо ref.current, так как, ref.current мутирует по своей исходной ссылке, когда ссылка присваивается DOM и useEffect в вашемПользовательский хук запускается после первого рендера, поэтому buttonRef.current будет ссылаться на узел DOM

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

Рабочая демонстрация

...