当 Object 不在 DOM 中时,如何修复无法读取 React 钩子中 null 的 querySelector

Posted

技术标签:

【中文标题】当 Object 不在 DOM 中时,如何修复无法读取 React 钩子中 null 的 querySelector【英文标题】:How to fix cannot read querySelector of null in React hook when Object is not in DOM 【发布时间】:2021-02-12 00:23:47 【问题描述】:

组件/VideoPlayer.js

import '../../style/VideoPlayer/VideoPlayer.css';
const VideoPlayer = () => ...some code...return(...some code...) export default VideoPlayer

VideoContainer.js

import VideoPlayer from "../../VideoPlayer"
import useVideoPlayer from "../../../hooks/useVideoPlayer"

const VideoContainer = ( info ) => 

const  playPause, toggleFullScreen, fullscreen  = useVideoPlayer()
return (
<React.Fragment>
<div>
...
/* playPause == true ? (
            <div><VideoPlayer/></div>)
            : null */

fullscreen && <div id="testing"><VideoPlayer src=info.somesrc title=info.sometitle /></div>

/* <div id="testing"><VideoPlayer src=info.somesrc title=info.sometitle /></div> */
...
</div>
</React.Fragment>
)
import React,  useState, useEffect, useRef, useLayoutEffect  from 'react'
const useVideoPlayer = () => 
    const [playVideo, setPlayVideo] = useState(false)
    const [fullscreen, setFullscreen] = useState(false)

    useEffect(() => 
    const controlsContainer = document.querySelector('.video-container .controls-container');
    const playPauseButton = document.querySelector('.video-container .controls button.play-pause');
    const pauseButton = playPauseButton.querySelector('.paused'); // typeError cannot read property querySelector of null
    ...some other queryselectors...
    , []);

const displayControls = () => 
    let controlsTimeout;
    const controlsContainer = document.querySelector('.video-container .controls-container');
    ...


const playPause= () => 
  const video = document.querySelector('.video-container video');
  if (...) 
    video.play(); 
    setPlayVideo(true);
   else (...) 
    video.pause();
    setPlayVideo(false);
  ;


const toggleFullScreen = () => 
 const videoContainer = document.querySelector('.video-container');
 if (...) setFullscreen(true) else (...) setFullscreen(false);


...
document.addEventListener('fullscreenchange', () => 
...


const video = document.querySelector('.video-container video');

video && (video.addEventListener('timeupdate', () => 
...


...a dozen other addEventListeners for different elements fetched by querySelectors...

return  playPause, toggleFullScreen, fullscreen 



export default useVideoPlayer;

我将 playPausetoggleFullscreen 函数和 fullscreen reactcontext 从 useVideoPlayer 挂钩传回反应容器。 我使用&amp;&amp; 并检查fullscreen 是真还是假来渲染&lt;VideoPlayer/&gt;

当组件不在 DOM 中时,useVideoPlayer 挂钩不起作用,我该如何解决?

Uncaught TypeError: Cannot read property 'querySelector' of null
    at eval (useVideoPlayer.js:41)
    at commitHookEffectListMount (react-dom.development.js:19731)
    at commitPassiveHookEffects (react-dom.development.js:19769)
    at htmlUnknownElement.callCallback (react-dom.development.js:188)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:237)
    at invokeGuardedCallback (react-dom.development.js:292)
    at flushPassiveEffectsImpl (react-dom.development.js:22853)
    at unstable_runWithPriority (scheduler.development.js:653)
    at runWithPriority$1 (react-dom.development.js:11039)
    at flushPassiveEffects (react-dom.development.js:22820)

我试过useLayoutEffect,传递useRef(null)并包裹if (containerRef.current) ,但没有区别

【问题讨论】:

【参考方案1】:

您需要在尝试使用之前检查该元素是否存在:

const pauseButton = playPauseButton && playPauseButton.querySelector('.paused'); 

如果您想要更具可扩展性的东西,您可以在 VideoContainer 中明确控制是否渲染,然后将其传递给您的钩子:

const VideoContainer = ( info ) => 
    const [mounted, setMounted] = useState(false)
    const  playPause, toggleFullScreen, fullscreen  = useVideoPlayer(mounted)
    return (
    ...
    mounted ? <VideoPlayer/> : null

【讨论】:

这是相当多的检查。我有十几个 querySelector,它们被十几个其他函数和 React 钩子中的 addEventListeners 使用。有没有更短的方法来切换这种变化? 我用条件 mounted 包裹了钩子,并将 onClick=() =&gt; mounted ? setMounted(false) : setMounted(true) ; 添加到其父容器中。但我仍然收到Uncaught (in promise) ReferenceError: playPause is not defined,因为这些返回值无效 你能在某处添加一个简单的用例演示吗? 你能看到codesandbox.io/s/modest-thunder-pev51?file=/src/App.js 所以也许挂载是错误的方式,我认为你应该在尝试访问属性之前更加小心地检查存在codesandbox.io/s/sad-smoke-0t1oj

以上是关于当 Object 不在 DOM 中时,如何修复无法读取 React 钩子中 null 的 querySelector的主要内容,如果未能解决你的问题,请参考以下文章

而(真);当不在 void 中时,循环会抛出无法访问的代码

当节点不在视图中时如何删除它?

当 XAMPP 不在分区的根目录中时,为啥它不能工作?

当 flexbox 内容垂直居中时如何修复滚动? [复制]

当用户不在聊天中时如何显示发送的最后一条消息?

swift - 当persistentContainer不在App委托方法中时如何从核心数据中删除所有数据