React钩子,如何在组件卸载时触发一些逻辑

时间:2019-03-07 08:03:38

标签: 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) &&
      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 ],但是方法videoProgress中的saveStateToLocalStorage不会得到更新,并且onPause方法不会导致正确保存到localStorage(它将保存0)。有没有办法用钩子而不是在componentWillUnmount生命周期中使用类组件?

1 个答案:

答案 0 :(得分:2)

您必须了解useEffect的工作原理。

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

每当依赖项更改时,请重新运行useEffect。您的videoProgress值会在时间间隔后发生变化(可能不超过1秒,我不知道)。这就是为什么react重新运行useEffect的原因。

您可以这样做。

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

或者您可以添加视频ID的相关性(根据要求)。


一个方便的例子

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

一个侧面说明:您可以具有多个useEffect。