Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't see state inside method helper function using hooks

Tags:

I cannot see the updated state inside of the helper method I'm using. All those worked in the class-based component, but it seems to be not the same when using hooks. Check out my comments.

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

import { VIMEO_URL } from '../../consts/urls';
import storage from '../../utils/localStorage';

const STORAGE_VIDEOS_DATA_KEY = 'VIDEOS_DATA';

import './VideoItem.scss';


const VideoItem = ({ vimeoId }) => {
  useEffect(() => {
    window.addEventListener(
      'beforeunload',
      saveStateToLocalStorage
    );

    return () => {
      window.removeEventListener(
        'beforeunload',
        saveStateToLocalStorage
      );

      saveStateToLocalStorage();
    };
  }, []);


  const [ videoProgress, setVideoProgress ] = useState(0);

  const saveStateToLocalStorage = () => {
    const videosPlayedDuration = {
      [vimeoId]: videoProgress, // here I'm not getting updated videoProgress state, only default value
    };

    // here I will save videosPlayedDuration to the storage
  };

  return createPortal(
    <div className="video-modal-background" onClick={onVideoClose}>
      <div className="video-modal-window">
        <ReactPlayer
          playing={true}
          url={VIMEO_URL + vimeoId}
          onProgress={videoProgress => setVideoProgress(videoProgress.playedSeconds)} // here I'm setting state
        />
      </div>
    </div>,
    document.getElementById('modal-root')
  );
};

export default VideoItem;

So the as you can see I'm trying to use updated state but all I'm getting there is 0 as a default state.

like image 332
Murakami Avatar asked Mar 25 '19 10:03

Murakami


People also ask

Can I use Hooks inside functions?

Don't call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. By following this rule, you ensure that Hooks are called in the same order each time a component renders.

How do you update state with Hooks?

To update the state, call the state updater function with the new state setState(newState) . Alternatively, if you need to update the state based on the previous state, supply a callback function setState(prevState => newState) .


1 Answers

If you want to have componentWillUnmount behavior use useRef to access the updated values inside your listener:

   const [ videoProgress, setVideoProgress ] = useState(0);
   const videoProgressRef = useRef(videoProgress);
   useEffect(() => videoProgressRef.current = videoProgress, [videoProgress]);

   function saveStateToLocalStorage(){
      const videosPlayedDuration = {
        [vimeoId]: videoProgressRef.current, 
      };
   }

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

    return () => {
      window.removeEventListener(
        'beforeunload',
        saveStateToLocalStorage
      );

      saveStateToLocalStorage();
    };
  }, []);

If you use saveStateToLocalStorage only inside useEffect it is better to move it inside the callback of useEffect. So it doesn't get recreated every render:

  useEffect(() => {
    function saveStateToLocalStorage(){
        const videosPlayedDuration = {
          [vimeoId]: videoProgressRef.current, 
        };
    }

    window.addEventListener(
      'beforeunload',
      saveStateToLocalStorage
    );

    return () => {
      window.removeEventListener(
        'beforeunload',
        saveStateToLocalStorage
      );

      saveStateToLocalStorage();
    };
  }, []);
like image 85
UjinT34 Avatar answered Oct 18 '22 15:10

UjinT34