Перехватчики React, как запустить некоторую логику один раз при размонтировании компонента

#reactjs

#reactjs

Вопрос:

Сейчас я играю с хуками. Мой код выглядит следующим образом:

     import React, { Fragment, useState, useEffect, useRef } from 'react';
import ReactPlayer from 'react-player';

import storage from '../../utils/localStorage';

const STORAGE_VIDEOS_DATA_KEY = 'VIDEOS_DATA';

const VideoItem = () => {
  const [ playingStatus, setPlayingStatus ] = useState(false);
  const [ videoId ] = useState(318298217);
  const [ videoProgress, setVideoProgress ] = useState(0);
  const player = useRef();

  useEffect(() => {
    window.addEventListener(
      'beforeunload',
      saveStateToLocalStorage
    );

    return () => {
      window.removeEventListener(
        'beforeunload',
        saveStateToLocalStorage
      );
      saveStateToLocalStorage(); // this needs to be fired only once when component onmounts but clearing effect is done every time videoProgress changes
    };
  }, [videoProgress]);

  const onVideoEnd = () => {
    console.log('backend call - video end status');
  };

  const seekToPoint = () => {
    const videosData = storage.hasKey(STORAGE_VIDEOS_DATA_KEY) amp;amp;
      JSON.parse(storage.getItem(STORAGE_VIDEOS_DATA_KEY));
    const toReturnVideoPoint = videosData[videoId] || 0;

    player.current.seekTo(Number(toReturnVideoPoint));
  };

  const saveStateToLocalStorage = () => {
    const videosPlayedDuration = {
      [videoId]: videoProgress,
    };

    storage.setItem(STORAGE_VIDEOS_DATA_KEY, JSON.stringify(videosPlayedDuration));
  };

  return (
    <Fragment>
      <ReactPlayer
        ref={player}
        playing={playingStatus}
        url={`https://player.vimeo.com/video/${videoId}`}
        onPause={() => {
          saveStateToLocalStorage();
          setPlayingStatus(false);
        }}
        onEnded={onVideoEnd}
        onProgress={progress => setVideoProgress(progress.playedSeconds)}
      />
      <button onClick={() => {
        seekToPoint();
        setPlayingStatus(true);
      }}
      >
        GO BACK TO THE PREVIOUS POINT
      </button>
    </Fragment>
  );
};

export default VideoItem;
  

Я хочу очищать свой эффект только при размонтировании компонента, а не каждый раз, когда компонент повторно отображается при изменении состояния части видеопрогресса. Я мог бы просто использовать пустой массив вместо [ videoProgress ] но тогда videoProgress in метод saveStateToLocalStorage не будет обновляться, и onPause метод не приведет к надлежащему сохранению в localStorage (это сохранит 0). Есть ли способ сделать это с помощью хуков, не используя class component с componentWillUnmount жизненным циклом?

Ответ №1:

Вы должны понимать, как useEffect работает.

 useEffect(() => {
    // your code
    return () => {
      // your clearup code 
    };
  }, [videoProgress]); // => This is the key point. It's called dependency
  

Всякий раз, когда зависимость изменяется, react повторно запускает useEffect . Ваше videoProgress значение изменяется через интервал времени (может быть, через 1 секунду, я не знаю). Вот почему react повторно запускает useEffect.

Вы можете сделать это.

 useEffect(() => {
    // your code
    return () => {
      // your clearup code. =>> This block will work as componentWillUnmount
    };
  }, []); // => No Dependency
  

Или вы можете добавить зависимость от вашего идентификатора видео (в соответствии с вашими требованиями).


Один удобный пример

 useEffect(() => {
    // This block (before the return) will act as componentDidMount
    return () => {
      // This block will work as componentWillUnmount
    };
  }, []); // => beacase of No Dependency

  

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

Комментарии:

1. В дополнение к этому у вас может быть несколько useEffect функций, так что у вас может быть одна, которая запускается один раз, передавая пустой массив, а другая, которая выполняет то, что должно быть сделано, когда videoProgress изменилось.

2. Обновленный Anwser с несколькими дополнительными деталями

3. Хорошо, я понял, но когда я использую пустой массив [ ], мой компонент не обновляется, и в методе saveStateToLocalStorage сохраняется начальное значение 0.