当 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;
我将 playPause
、toggleFullscreen
函数和 fullscreen
reactcontext 从 useVideoPlayer
挂钩传回反应容器。
我使用&&
并检查fullscreen
是真还是假来渲染<VideoPlayer/>
当组件不在 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=() => 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的主要内容,如果未能解决你的问题,请参考以下文章