ComponentWillUnmount с хуком React useEffect

Как можно использовать крючок useEffect (или любой другой крючок, если уж на то пошло) для воспроизведения componentWillUnmount?

В традиционном компоненте класса я бы сделал что-то вроде этого:

class Effect extends React.PureComponent {
    componentDidMount() { console.info("MOUNT", this.props); }
    componentWillUnmount() { console.info("UNMOUNT", this.props); }
    render() { return null; }
}

С крючком useEffect:

function Effect(props) {
  React.useEffect(() => {
    console.info("MOUNT", props);

    return () => console.info("UNMOUNT", props)
  }, []);

  return null;
}

(Полный пример: https://codesandbox.io/s/2oo7zqzx1n)

Это не работает, так как функция «очистки», возвращенная в useEffect, фиксирует реквизиты в том виде, в каком они были во время монтирования, а не состояние реквизитов во время размонтирования.

Как я могу получить последнюю версию реквизита в useEffect очистке без, запускающей тело функции (или очистку) при каждом изменении реквизита?

Аналогичный вопрос не касается части доступа к последним реквизитам.

Состояние реагировать документы:

If you want to run an effect and clean it up only once (on mount and unmount), you can pass an empty array ([]) as a second argument. This tells React that your effect doesn’t depend on any values from props or state, so it never needs to re-run.

Однако в этом случае я завишу от реквизита... но только для части очистки...

Что вы хотите сделать, когда компонент размонтирован? Может быть, есть какой-то другой способ сделать это.

Tholle 13.03.2019 11:28

Это более общий вопрос, так как мне интересно, есть ли случаи, которые еще невозможно воспроизвести с помощью хуков. Но конкретным примером может быть сохранение реквизита в localStorage, IndexDB или аналогичном при размонтировании и чтение их обратно при монтировании. Я не думаю, что это следует делать при каждом изменении реквизита (например, при нажатии клавиши (если мы отображаем поля ввода)

ChrisG 13.03.2019 11:31
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
87
2
79 806
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Вы можете использовать useRef и хранить реквизиты, которые будут использоваться в замыкании, таком как метод обратного вызова render useEffect return.

function Home(props) {
  const val = React.useRef();
  React.useEffect(
    () => {
      val.current = props;
    },
    [props]
  );
  React.useEffect(() => {
    return () => {
      console.info(props, val.current);
    };
  }, []);
  return <div>Home</div>;
}

ДЕМО

Однако лучший способ - передать второй аргумент useEffect, чтобы очистка и инициализация происходили при любом изменении желаемых реквизитов.

React.useEffect(() => {
  return () => {
    console.info(props.current);
  };
}, [props.current]);

Большое спасибо! Использование useRef работает. Я также применил вашу технику к исходному примеру (упомянутому в вопросе) здесь: codeandbox.io/s/48w5m9pkwx для справочных целей.

ChrisG 13.03.2019 11:39

Хотя это правильный ответ... он по-прежнему крайне неэргономичен, особенно когда вы также зависите от местного штата. Иногда лучше использовать компонент класса.

hasufell 21.11.2019 09:20

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

Tom 30.07.2020 21:06

Вот ссылка на соответствующую документацию: reactjs.org/docs/hooks-effect.html#effects-with-cleanup. Из документов: «Если ваш эффект возвращает функцию, React запустит ее, когда придет время очистить:»

ethaning 19.10.2020 19:43

useLayoutEffect() — ваш ответ в 2021 году

useLayoutEffect(() => {
    return () => {
        // Your code here.
    }
}, [])

Это эквивалентно ComponentWillUnmount.

В 99% случаев вы хотите использовать useEffect, но если вы хотите выполнить какие-либо действия перед размонтированием DOM, вы можете использовать предоставленный мной код.

Извините, но это не решает проблему, которую я поднял. Мне нужен доступ к реквизиту в момент размонтирования, и useLayoutEffect ведет себя так же, как useEffect в отношении запуска только при изменении зависимостей (пустой массив, который вы передали). Поэтому ваше решение имеет доступ только к начальным реквизитам FunctionComponent, а не к «последним реквизитам» во время размонтирования.

ChrisG 01.04.2021 19:35

Ах извините! В то утро я не спал до 6, так что я не читал внимательно, ха-ха.

Simen L 02.04.2021 10:27

Я прочитал первые два вопроса и ответил на них. Да, вам нужно поместить переменную, на которой вы хотите сосредоточиться, в useRef для прямого доступа к новейшим изменениям. Функция возврата useEffect() фактически запускается после повторного рендеринга (делает DOM недоступным), в то время как функция возврата useLayoutEffect() запускается перед повторным рендерингом (делает DOM доступным). Это было самое чистое решение, которое я нашел, и я думал, что оно похоже на ваше. Возможно, это все равно поможет в будущем :)

Simen L 02.04.2021 10:38
useEffect(() => {
  if (elements) {
    const cardNumberElement =
      elements.getElement('cardNumber') ||  // check if we already created an element
      elements.create('cardNumber', defaultInputStyles); // create if we did not
            
    cardNumberElement.mount('#numberInput');
  }
}, [elements]);

useLayoutEffect отлично подходит для очистки прослушивателей событий на узлах DOM.

В противном случае с обычным useEffect ref.current будет нулевым при срабатывании ловушки времени.

Подробнее о реагирующих документах https://reactjs.org/docs/hooks-reference.html#uselayouteffect

  import React, { useLayoutEffect, useRef } from 'react';

  const audioRef = useRef(null);


  useLayoutEffect(() => {
    if (!audioRef.current) return;

    const progressEvent = (e) => {
      setProgress(audioRef.current.currentTime);
    };

    audioRef.current.addEventListener('timeupdate', progressEvent);

    return () => {
      try {
        audioRef.current.removeEventListener('timeupdate', progressEvent);
      } catch (e) {
        console.warn('could not removeEventListener on timeupdate');
      }
    };
  }, [audioRef.current]);



Прикрепить ссылку к узлу DOM компонента

<audio ref = {audioRef} />

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