Разница между useMemo и useEffect с зависимостями Ref

В React и useEffect, и useMemo имеют аргумент зависимости.

Я наивно полагал, что они работают одинаково: всякий раз, когда значения в этом аргументе зависимостей изменялись, я думал, что будет выполняться обратный вызов useEffect или useMemo, и что единственная разница заключалась во времени (useMemo выполняется до рендеринга, а useEffect — после).

В целом они идентичны. Но когда у меня есть ссылка в качестве зависимости, это не так:

const Foo = ()=> {
  const ref = useRef();
  useEffect(() => console.info('effect ref', ref), [ref]);
  useMemo(() => console.info('memo ref', ref), [ref]);
  return <div ref = {ref}>Foo</div>
}

Ссылка на песочницу (нажмите значок консоли в правом верхнем углу, чтобы просмотреть журналы)

Насколько я понимаю, Foo должен рендериться дважды. В первый раз ref.current будет undefined, а во второй раз будет установлено <div>. Таким образом, я ожидаю увидеть два useEffect журнала effect ref div, потому что ref будет установлен как после рендеринга №1, так и после рендеринга №2. И я делаю ...

ссылка на эффект {текущий: div}

ссылка на эффект {текущий: div}

Однако в журналах заметок я никогда не вижу зарегистрированных div. Вместо этого я просто вижу повторение одного и того же:

ссылка на заметку не определена

ссылка на заметку не определена

Это ожидаемо для первого вызова, поскольку компонент еще не отрендерился... но перед вторым рендерингом, после первого, разве ref не должно быть установлено на <div>, и поэтому не следует useMemo регистрировать его? Вместо этого, похоже, useMemo никогда не запускается.

Короче говоря, хотя я в основном понимаю, как работает useMemo, похоже, мне не хватает некоторых ключевых деталей, объясняющих, почему он никогда не регистрирует значение ref, и я был бы признателен за любую помощь, объясняющую почему.

P.S. Я понимаю, что, вероятно, мог бы решить все это, сделав useRef зависимым от переменной состояния, а затем создав useEffect, который обновляет эту переменную состояния при изменении ref... но я больше сосредоточен на том, чтобы сначала попытаться понять проблему, прежде чем решить ее. это.

Однако ссылка является стабильной ссылкой на объект, поэтому даже если значение current внутри изменится, внешняя ссылка останется прежней. Поэтому я бы не ожидал, что заметка или эффект сработают дважды. Ссылка на песочницу, похоже, не работает, но возможно ли, что StrictMode мешает, запуская их дважды на одном и том же рендеринге?

Brian Thompson 23.05.2024 21:58

Но независимо от того, является ли ref стабильным объектом или нет, я делаю два рендеринга, верно? На рендере №1 установлен ref.currentundefined, а на рендере №2 (и 3, и...) установлен ref.current. Если я правильно понимаю, то если я вижу два журнала обратных вызовов useMemo, это должно означать, что useMemo запускался один раз перед первым рендерингом и один раз перед вторым. Разве этот второй не должен был зарегистрировать ref, поскольку к тому времени он уже должен был быть установлен? Я знаю, что что-то здесь не понимаю, но не знаю, что именно.

machineghost 23.05.2024 22:03

Я предполагаю, что он не выполняет два рендеринга или делает два рендеринга только из-за React.StrictMode, который может вести себя иначе, чем обычный повторный рендеринг.

Brian Thompson 23.05.2024 22:08

Я считаю, что ваш эффект и заметка в основном настроены на запуск только один раз, но вы видите, что они выполняются дважды. Единственное объяснение, которое я видел в мире React, — это строгий режим. И сложно понять нормальный цикл событий в строгом режиме, поэтому я хотел сначала обсудить это, чтобы исключить

Brian Thompson 23.05.2024 22:10

О, кажется, теперь я понимаю. Если я вас поймаю, вы говорите, что журналы, которые я вижу, отражают несколько рендеров до DOM (т. е. предварительной возможности установки ссылки)? Я думал, что компонент визуализируется только один раз без ref, а ref всегда устанавливался рендером №2... но вы предполагаете, что я вижу журналы нескольких рендерингов до того, как был установлен ref (потому что «React Strict Mode» вызывает второй рендеринг с предварительной настройкой)?

machineghost 23.05.2024 22:10

Честно говоря, сложно уследить за всеми странностями, которые может вызвать StrictMode, но пока мы это обсуждаем, я считаю, что в React 18 добавили новую функцию, которая вызывает двойное монтирование компонентов, что определенно может объяснить такое поведение. Позвольте мне посмотреть, смогу ли я найти ссылку на документацию по этому поведению.

Brian Thompson 23.05.2024 22:13

Если бы вы могли добавить эту ссылку в ответ, это определенно показалось бы лучшим объяснением, которое кто-либо представил, поэтому я с радостью проголосую за него (и приму его через 24 часа, если ничего больше не появится).

machineghost 23.05.2024 22:14

К сожалению, я не смог найти эту информацию в документации, только в объявлении о выпуске React 18 - act.dev/blog/2022/03/29/react-v18#new-strict-mode-behavior‌​s - " Эта новая проверка автоматически размонтирует и перемонтирует каждый компонент при первом монтировании компонента, восстанавливая предыдущее состояние при втором монтировании».

Brian Thompson 23.05.2024 22:18

В документации есть упоминание об этом здесь — act.dev/reference/react/… — «Когда включен строгий режим, React также запускает один дополнительный цикл настройки + очистки в разработке для каждого эффекта. Это может показаться удивительным, но это помогает выявить тонкие ошибки, которые трудно обнаружить вручную».

Brian Thompson 23.05.2024 22:21
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
1
9
71
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Эффект использования вызовет действие, основанное на измененной ссылке, где has memo будет переменной, и вам нужно будет что-то вернуть.

Например:


const Foo = ()=> {
  const user = authHook(); // whatever here

  useEffect(() => console.info('user full name', `${user.firstname} ${user.lastname}` ), [user]); // will trigger when the user changes and outputs to console the user fill name

  const fullname = useMemo(() => `${user.firstname} ${user.lastname}`, [user]); // returns a variable that can be used in your code and will auto update when the user changes but wont reivaluate when anything else changes

  return <div>{fullname}</div>
}

Я думаю, что в вашем вопросе, в частности, происходит то, что вы передаете ссылку, хотя должны пройти ref.current

Спасибо, но опять же я не ищу решения, я ищу объяснение того, как useMemo работает, в частности, как он обрабатывает ref зависимости иначе, чем useEffect.

machineghost 23.05.2024 21:50

тогда это может ответить на ваш вопрос, stackoverflow.com/a/67906087/10037470 кажется, что есть проблемы с useMemo и useEffect с ссылками, поэтому рекомендуется использовать useCallback.

Devon Ray 23.05.2024 21:54

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

machineghost 23.05.2024 22:07
Ответ принят как подходящий

Такое поведение связано с React.StrictMode, а не с использованием ссылки в качестве зависимости.

Ссылка — это стабильная ссылка на объект при рендеринге, а useEffect и useMemo используют проверку ссылочного равенства, чтобы определить, изменились ли их зависимости. Следовательно, при нормальных обстоятельствах (например, при сборке продукта) эти перехватчики будут выполняться только один раз.

Поскольку вы видите свидетельства того, что они запускаются более одного раза, ответ — строгий режим.

Объяснение этому можно найти в нескольких различных разделах документации.

https://react.dev/reference/react/StrictMode

Ваши компоненты будут перезапускать эффекты еще раз, чтобы найти ошибки, вызванные отсутствием очистки эффектов.

https://react.dev/reference/react/useMemo#caveats

В строгом режиме React дважды вызовет вашу вычислительную функцию, чтобы помочь вам найти случайные примеси. Это поведение предназначено только для разработки и не влияет на производство. Если ваша вычислительная функция чистая (как и должно быть), это не должно повлиять на вашу логику. Результат одного из вызовов будет проигнорирован.

https://react.dev/blog/2022/03/29/react-v18#new-strict-mode-behaviors

Также возможно (но невозможно сказать), что это может быть вызвано тем, что React 18 StrictMode перемонтирует каждый компонент.

Другие вопросы по теме