Возможно ли использование ссылки в пользовательском хуке или рекомендуется для изменения DOM? - PullRequest
2 голосов
/ 13 февраля 2020

Я работаю над включением сторонней библиотеки в приложение React, и ловушки делают это очень простым в целом. Однако я столкнулся с несколькими проблемами, и я надеялся на некоторую ясность в отношении того, что происходит «под капотом».

Для простоты, скажем, это мой сторонний код, который напрямую изменяет DOM:

const renderStuff = (div, txt) => {
  if(div) div.innerHTML = txt;
}

И мой компонент выглядит примерно так:

export const EffectRender = () => {
  const divRef = useRef();
  useRenderer(divRef, "Hello, world");
  return <div ref={divRef}></div>;
}

Вот предлагаемый пользовательский хук:

const useRenderer = (ref, txt) => {
  const div = ref.current;
  useEffect(() => {
    renderStuff(div, txt);
  },[div, txt])
};

Это работает, если один из параметров ( в этом случае txt) обновляется поздно, скажем, в результате асинхронной загрузки c. Но useEffect никогда не распознает изменение значения ref.current. Поэтому, если txt задано до установки ref.current (как в этом случае), компонент никогда не рендерится.

Я понимаю, что могу это исправить, используя setState в пользовательском хуке, как в этот пример . Но это начинает казаться громоздким.

Я также понимаю, что мог бы поставить вызов renderStuff в useEffect ловушку на главном компоненте, и это гарантирует, что установлен ref.current. Итак: useEffect(() => { renderStuff(divRef.current, txt); },[txt]); хорошо.

Мой вопрос на самом деле заключается в том, является ли весь этот подход использования ссылки внутри пользовательского хука хорошей идеей. Есть ли более простой способ заставить крючок распознавать, когда ссылка изменилась? Или это тот случай, когда пользовательские хуки не подходят для этой задачи?

Ответы [ 2 ]

1 голос
/ 14 февраля 2020

Проблема в том, что const div = ref.current; в useRenderer объявлено вне хука. В этот момент цикла ссылка все еще не назначена, поэтому ее значение равно null.

Если я правильно понял проблему, то решение состоит в том, чтобы просто переместить ссылку в обратный вызов useEffect. Это одно из ваших предложений, и я считаю, что это правильный путь:

const useRenderer = (ref, txt) => {
    useEffect(() => {
        const div = ref.current;
        renderStuff(div, txt);
    }, [txt]);
};

useEffect зависимости не сработают при изменении ссылки. Это нормально в вашем случае, так как ссылка уже имеет присвоенное ей значение при запуске useEffect. Однако, если ссылка изменилась, и вам нужно было отследить изменение, путь к go использует useState.

0 голосов
/ 16 февраля 2020

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

Мои плохие логи c

Мы используем effect крючки, потому что код изменяет DOM, и useEffect существует для обработки именно такого рода побочных эффектов.

Мы используем ref перехватчики для соединения с данным элементом DOM. Экземпляр ref похож на одноэлементный, в этом случае экземпляр не изменяется в течение жизни приложения. Изменяется только свойство ref.current.

И непосредственно под соответствующим разделом в справочных документах о перехватах Я прочитал это:

Имейте в виду, что useRef не не уведомит вас, когда его содержание изменится. Отключение свойства .current не вызывает повторного рендеринга.

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

Кстати: это мышление было подкреплено es-lint с требованием добавить ref.current (в моем случае div = ref.current) в списке зависимостей: React Hook useEffect has a missing dependency: 'div'. Either include it or remove the dependency array. Я всегда доверяю линтеру, конечно.

Мой вывод: не было простого способа использовать effect ловушку для сделать в ref экземпляр. Мне нужно было как-то поместить ref.current в useState сеттер. Ужасно!

Мое основное предположение

Я предположил, что способ useEffect обрабатывать побочные эффекты не имеет значения. Крюк effect - это черный ящик, скрытый в недрах исходного кода typescript, и он движется таинственными способами.

Но единственное, что «похоронили», было это предложение в use-effect docs :

Функция, переданная useEffect, запустится после фиксации рендера на экране.

" После рендер ». Конечно. То есть как useEffect обрабатывает побочные эффекты, гарантируя, что не будет работать, пока DOM не будет готов к go.

Решение

Как следует из ответа Альваро Обязательно прочитайте свойство ref.current внутри useEffect hook. ref никогда не меняется, и ref.current гарантированно будет уже заполнен элементом DOM.

Как обычно, это очевидно задним числом. Еще раз спасибо, Альваро.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...