简体   繁体   中英

React component does not re-render if there is only one state update

I have 2 components -> a) VideoJSPlayer and b) VideosPage.

As the name suggests, VideoJsPlayer has the video player part (using videojs) and VideosPage shows videos using VideoJsPlayer component.

The code for each of the components is:

a) VideoJsPlayer

import React, { useEffect } from "react";
import videojs from "video.js";

const VideoJsPlayer = props => {
  let player = null;
  let videoNode = null;

  // Effect for first render only.
  useEffect(() => {
    player = videojs(videoNode, props, function onPlayerReady() {
      console.log("onPlayerReady");
    });
    return function cleanup() {
      if (player) {
        player.dispose();
      }
    };
  }, []);

  return (
    <div>
      <div data-vjs-player>
        <video ref={node => (videoNode = node)} className="video-js"></video>
      </div>
    </div>
  );
};

export default VideoJsPlayer;

b) VideosPage

import React, { useState, useEffect } from "react";
import videojs from "video.js";
import VideoJsPlayer from "../../components/VideoJsPlayer";

const VideosPage = props => {
  const [videoJsOptions, setVideoJsOptions] = useState({});

  // Effect for first render only.
  useEffect(() => {
    let videoJsOptionsTest = {
      autoplay: false,
      controls: true,
      sources: [
        {
          src: "/my-video.mp4",
          type: "video/mp4"
        }
      ]
    };

    console.log("test");

    setVideoJsOptions(videoJsOptionsTest);

    // Cleanup on unmount.
    return function cleanup() {};
  }, []);

  return (
    <React.Fragment>
      <VideoJsPlayer {...videoJsOptions} />
    </React.Fragment>
  );
};

export default VideosPage;

When I check the URL which renders the VideosPage component, I don't see any video. I'm assuming the state (videoJsOptions) is not yet updated in VideosPage and hence I don't see the video. I can see the console log - "test" though.

When I change the code of VideosPage to:

import React, { useState, useEffect } from "react";
import videojs from "video.js";
import VideoJsPlayer from "../../components/VideoJsPlayer";

const VideosPage = props => {
  const [videoJsOptions, setVideoJsOptions] = useState({
    autoplay: false,
    controls: true,
    sources: [
      {
        src: "/my-video.mp4",
        type: "video/mp4"
      }
    ]
  });

  // Effect for first render only.
  useEffect(() => {
    /*
    let videoJsOptionsTest = {
      autoplay: false,
      controls: true,
      sources: [
        {
          src: "/my-video.mp4",
          type: "video/mp4"
        }
      ]
    };

    console.log("test");

    setVideoJsOptions(videoJsOptionsTest);
    */

    // Cleanup on unmount.
    return function cleanup() {};
  }, []);

  return (
    <React.Fragment>
      <VideoJsPlayer {...videoJsOptions} />
    </React.Fragment>
  );
};

export default VideosPage;

Now when I check the URL which renders the VideosPage component, I see the video.

My assumption is - In the first case, the state update is queued and never happens. Because if it did happen, it should have re-rendered the component and I should have seen my video.

I have also tried updating useEffect in VideoJSPlayer to run every time props changes, but it didn't help.

Any insights on this?

Thank you for your time.

Here is a sandbox : codesandbox

You have some mistakes:

  1. You can't use videoNode like that because Video.js uses Ref and since you are using React hooks you need to use useRef
import React, { useRef, useEffect } from "react"

const VideoJsPlayer = props => {
  const videoRef = useRef(null)

  useEffect(() => {
    //...
    videojs(
      videoRef.current,
      props,
      function onPlayerReady() {
        console.log("onPlayerReady")
      }
    )
   //...
  }, [])

 return (
   <video ref={videoRef} className="video-js" />
 )
}
  1. You don't need to use useEffect again in your VideosPage component since its already been used inside your VideoJsPlayer component. Your initial options need to set directly in the state or outside the function like so:
const initialOptions = {
 // ...
}

const VideosPage = () => {
  return <VideoJsPlayer {...initialOptions} />
}

Here is a working codeSandbox example based on your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM