Компонент React не перерисовывается, если есть только одно обновление состояния

У меня есть 2 компонента -> а) VideoJSPlayer и б) VideosPage.

Как следует из названия, VideoJsPlayer имеет часть видеоплеера (с использованием videojs), а VideosPage показывает видео с использованием компонента VideoJsPlayer.

Код для каждого из компонентов:

а) ВидеоJsPlayer

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;

б) страница видео

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;

Когда я проверяю URL-адрес, отображающий компонент VideosPage, я не вижу никакого видео. Я предполагаю, что состояние (videoJsOptions) еще не обновлено в VideosPage, и поэтому я не вижу видео. Я вижу журнал консоли - "тест".

Когда я меняю код VideosPage на:

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;

Теперь, когда я проверяю URL-адрес, отображающий компонент VideosPage, я вижу видео.

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

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

Любые идеи по этому поводу?

Спасибо за ваше время.

Вот песочница: codesandbox


person Raman Kishore    schedule 01.02.2020    source источник
comment
это не то, как вы используете ref, читайте о useRef   -  person Dennis Vash    schedule 01.02.2020
comment
@DennisVash: Не могли бы вы уточнить? Обратный вызов передается ссылке, как показано в документации здесь — docs.videojs.com/ учебник-реакция.html. Если была проблема с ref, я предполагаю, что это не должно было работать и во втором случае. Пожалуйста, дайте мне знать, если я что-то здесь упустил.   -  person Raman Kishore    schedule 01.02.2020
comment
Проблема с рефом, можете сделать песочницу с этим примером? codeandbox.io   -  person Dennis Vash    schedule 01.02.2020
comment
Вот песочница того, что работает, а что нет - codesandbox.io /с/   -  person Raman Kishore    schedule 01.02.2020


Ответы (1)


У вас есть ошибки:

  1. Вы не можете использовать videoNode таким образом, потому что Video.js использует Ref, а поскольку вы используете хуки React, вам нужно использовать 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. Вам не нужно снова использовать useEffect в компоненте VideosPage, так как он уже использовался внутри вашего компонента VideoJsPlayer. Ваши начальные параметры должны быть установлены непосредственно в state или вне функции следующим образом:
const initialOptions = {
 // ...
}

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

Вот рабочий пример codeSandbox, основанный на вашем коде.

person awran5    schedule 01.02.2020
comment
Спасибо за ваше время. 1. Я согласен с необходимостью использования useRef в React Hooks. Но я считаю, что это не нужно, если вы передаете обратный вызов в ref, который обновляет другую переменную. Но спасибо за вашу рекомендацию, на основании которой я обновил код. 2. Причина, по которой это работает, заключается в том, что у вас есть initialOptions вне компонента, и это гарантирует, что он установлен. Это работает и для второго случая в моем вопросе, где initialOptions устанавливается внутри useState. Но это не работает, если вы используете initialOptions в качестве переменной состояния и устанавливаете ее внутри useEffect при монтировании компонента. - person Raman Kishore; 01.02.2020
comment
На самом деле вам не нужно повторно использовать useEffect, потому что он уже использовался внутри компонента VideoJsPlayer. Взгляните на пример codeSandbox, мы устанавливаем initPlayer внутри useEffect и устанавливаем зависимости на props, player, что означает, что useEffect будет перемонтироваться на основе этих изменений значений. props здесь относится к initialOptions - person awran5; 01.02.2020
comment
Вот песочница того, что работает, а что нет - codesandbox.io /с/ - person Raman Kishore; 01.02.2020
comment
В компоненте VideoJsPlayer проблем нет. Мой вопрос: если состояние VideosPage.js обновляется через useEffect, почему видео не отображается? - person Raman Kishore; 01.02.2020
comment
потому что нельзя использовать useEffect дважды для одной и той же цели :) вам нужно будет установить начальные значения вместо props, а не передавать его. - person awran5; 01.02.2020