在 React with React Hooks 上使用动态导入视频

Posted

技术标签:

【中文标题】在 React with React Hooks 上使用动态导入视频【英文标题】:Using dynamic import with videos on React with React Hooks 【发布时间】:2020-08-02 07:42:39 【问题描述】:

我正在制作一种背景为全屏视频的个人网站。 我需要为不同的屏幕尺寸加载不同的视频。我正在使用 React,并基于 React Hooks 制作组件。

这些是我的依赖版本:

    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-router-dom": "^5.1.2",
    "react-scripts": "3.4.1",

在我的组件“VideoBackground”中,我使用 useState 创建了一个状态,我将在其中存储视频源和海报图像的源,如下所示:

  const [video, setVideo] = useState(
    poster: null,
    src: null,
  );

我使用 useEffect 钩子作为 componentDidMount 函数,我将根据窗口宽度动态加载视频,并向浏览器中的事件侦听器添加一个函数,以便在每次调整大小时重新加载视频发生:

  useEffect(() => 
    async function handleResize() 
      setVideo(await loadVideo());
    

    handleResize();

    window.addEventListener("resize", handleResize);
    window.addEventListener("orientationchange", handleResize);

    return () => 
      window.removeEventListener("resize", handleResize);
      window.removeEventListener("orientationchange", handleResize);
    ;
  , []);

根据窗口宽度动态加载视频的函数“loadVideo”是一个异步函数,因为我使用的是 React 提供的 Dinamic Import 语法,这是一个 Promise(我猜这是最大的问题)。这是我的完整组件,“loadVideo”函数在开头:

“./styles”文件是一个基于 styled-component lib 的组件,我用它来设置我的 html 的样式,没关系(我猜)。但是“视频”组件是来自 html 5 的视频标签。

import React,  useState, useEffect  from "react";
import PropTypes from "prop-types";

import  Video, Overlay, Content  from "./styles";

const VideoBackground = ( children ) => 
  /*
   * Media query of the resource's intended media; this should be used only in a <picture> element
   *
   * Extra large devices (large laptops and desktops, 1200px and up)
   * Large devices (laptops/desktops, 992px and up)
   * Medium devices (landscape tablets, 768px and up)
   * Small devices (portrait tablets and large phones, 600px and up)
   * Extra small devices (phones, 600px and down)
   */
  async function loadVideo() 
    const  width  = getWindowDimensions();

    let posterPromise;
    let srcPromise;

    if (width > 1280) 
      posterPromise = import("../../assets/images/code720.jpg");
      srcPromise = import("../../assets/videos/code720.mp4");
     else if (width > 720) 
      posterPromise = import("../../assets/images/code480.jpg");
      srcPromise = import("../../assets/videos/code480.mp4");
     else if (width > 480) 
      posterPromise = import("../../assets/images/code320.jpg");
      srcPromise = import("../../assets/videos/code320.mp4");
     else 
      posterPromise = import("../../assets/images/code240.jpg");
      srcPromise = import("../../assets/videos/code240.mp4");
    

    const [ default: poster ,  default: src ] = await Promise.all([
      posterPromise,
      srcPromise,
    ]);

    return 
      poster,
      src,
    ;
  

  /*
   * It returns the width of a window's content area
   */
  function getWindowDimensions() 
    const  innerWidth: width  = window;

    return 
      width,
    ;
  

  const [video, setVideo] = useState(
    poster: null,
    src: null,
  );

  useEffect(() => 
    async function handleResize() 
      setVideo(await loadVideo());
    

    handleResize();

    window.addEventListener("resize", handleResize);
    window.addEventListener("orientationchange", handleResize);

    return () => 
      window.removeEventListener("resize", handleResize);
      window.removeEventListener("orientationchange", handleResize);
    ;
  , []);

  console.log("BEFORE RETURN", video);

  return (
    <>
      <Video playsInline autoPlay muted loop poster=video.poster>
        <source src=video.src type="video/mp4" />
      </Video>
      <Overlay />
      <Content>children</Content>
    </>
  );
;

VideoBackground.propTypes = 
  children: PropTypes.node.isRequired,
;

export default VideoBackground;

问题是背景视频没有播放。但是出现了海报图片,我不知道为什么只有海报图片起作用了。 我把那个console.log放在return之前试图找出发生了什么,这就是记录的内容:

BEFORE RETURN poster: null, src: null
BEFORE RETURN poster: null, src: null
BEFORE RETURN poster: "/static/media/code720.e66bf519.jpg", src: "/static/media/code720.83f62f35.mp4"
BEFORE RETURN poster: "/static/media/code720.e66bf519.jpg", src: "/static/media/code720.83f62f35.mp4"

如您所见,视频和图像已正常加载(但经过一段时间)。我认为问题在于“useState”第一次为我的状态属性分配了一个空值,然后,“useEffect”调用了“loadVideo”函数。

我第一次尝试将视频导入并分配给“useState”,并且正常工作!但是,我不能让视频以“useState”开头,因为我需要之前知道屏幕尺寸并下载相应的视频。

我还尝试将“loadVideo”函数与“useState”一起使用,如下所示:

const [video, setVideo] = useState(loadVideo());

但是,它不起作用,因为“loadVideo”是异步的。

有没有人有解决这个问题的方法,或者我想做的更好的方法?

谢谢!

【问题讨论】:

此错误也出现在我的控制台上:./src/components/VideoBackground/index.js 第 77:6 行:React Hook useEffect 缺少依赖项:'loadVideo'。要么包含它,要么移除依赖数组 react-hooks/exhaustive-deps 【参考方案1】:

import() 没有加载视频,它只是获取 URL。您可以同步进行。

import videoSrc720 from "../../assets/videos/code720.mp4";

if (width>1280) 
    src = videoSrc720;
 ...

【讨论】:

以上是关于在 React with React Hooks 上使用动态导入视频的主要内容,如果未能解决你的问题,请参考以下文章

如何在 React with Typescript 中使用和指定通用 Hooks?

React Typescript with hooks:最大更新深度超出错误

[React] Refactor a Class Component with React hooks to a Function

[React Testing] Test your Custom Hook Module with react-hooks-testing-library

React Hooks with React Router v4 - 我如何重定向到另一个路由?

React Redux with hooks - 身份验证和受保护的路由