TypeScript错误:audioPlayer.current可能为null的解决求助
解决React音频播放器TypeScript中"audioPlayer.current may be null"错误
关键修复步骤
为Ref添加明确类型标注
TypeScript需要知道ref关联的DOM元素具体类型,给每个useRef指定对应类型:audioPlayer对应HTML音频元素:const audioPlayer = useRef<HTMLAudioElement | null>(null);progressBar对应Range输入框:const progressBar = useRef<HTMLInputElement | null>(null);animationRef存储动画ID:const animationRef = useRef<number | null>(null);
访问Ref前必须做空值检查
所有使用ref.current的场景,先确认它不为null,可通过可选链?.或if判断实现,避免TS报错:- 在
useEffect中先校验ref存在,再读取音频时长:useEffect(() => { if (audioPlayer.current && progressBar.current) { const seconds = Math.floor(audioPlayer.current.duration); if (!isNaN(seconds)) { setDuration(seconds); progressBar.current.max = seconds.toString(); } } }, [audioPlayer.current?.readyState]); - 播放/暂停、进度更新等函数中,同样添加空值校验,比如
togglePlayPause:const togglePlayPause = () => { const prevValue = isPlaying; setIsPLaying(!prevValue); if (!prevValue && audioPlayer.current) { audioPlayer.current.play(); animationRef.current = requestAnimationFrame(whilePlaying); } else if (audioPlayer.current) { audioPlayer.current.pause(); if (animationRef.current) { cancelAnimationFrame(animationRef.current); } } };
- 在
完善State和函数参数的类型标注
明确变量类型让TS类型检查更精准:currentTrack类型为string | null:const [currentTrack, setCurrentTrack] = useState<string | null>(null);calculateTime参数指定为number:const calculateTime = (secs: number) => { ... }- 事件参数添加类型:
const changeTrack = (e: React.MouseEvent<HTMLInputElement>) => { ... }
修复后完整代码
import React, { useState, useRef, useEffect } from "react"; const AudioPlayer = () => { // state const [isPlaying, setIsPLaying] = useState(false); const [duration, setDuration] = useState(0); const [currentTime, setCurrentTime] = useState(0); const [currentTrack, setCurrentTrack] = useState<string | null>(null); // references - 添加明确的类型标注 const audioPlayer = useRef<HTMLAudioElement | null>(null); const progressBar = useRef<HTMLInputElement | null>(null); const animationRef = useRef<number | null>(null); useEffect(() => { // 先检查ref是否存在 if (audioPlayer.current && progressBar.current) { const seconds = Math.floor(audioPlayer.current.duration); // 避免NaN的情况 if (!isNaN(seconds)) { setDuration(seconds); progressBar.current.max = seconds.toString(); } } }, [audioPlayer.current?.readyState]); const togglePlayPause = () => { const prevValue = isPlaying; setIsPLaying(!prevValue); if (!prevValue && audioPlayer.current) { audioPlayer.current.play(); animationRef.current = requestAnimationFrame(whilePlaying); } else if (audioPlayer.current) { audioPlayer.current.pause(); if (animationRef.current) { cancelAnimationFrame(animationRef.current); } } }; const whilePlaying = () => { if (audioPlayer.current && progressBar.current) { progressBar.current.value = audioPlayer.current.currentTime.toString(); setCurrentTime(audioPlayer.current.currentTime); animationRef.current = requestAnimationFrame(whilePlaying); } }; // 为参数添加类型标注 const calculateTime = (secs: number) => { const minutes = Math.floor(secs / 60); const returnedMinutes = minutes < 10 ? `0${minutes}` : `${minutes}`; const seconds = Math.floor(secs % 60); const returnedSeconds = seconds < 10 ? `0${seconds}` : `${seconds}`; return `${returnedMinutes} : ${returnedSeconds}`; }; const changeRange = () => { if (audioPlayer.current && progressBar.current) { const newTime = parseFloat(progressBar.current.value); audioPlayer.current.currentTime = newTime; setCurrentTime(newTime); } }; // 为事件参数添加类型标注 const changeTrack = (e: React.MouseEvent<HTMLInputElement>) => { setCurrentTrack(e.target.value); togglePlayPause(); }; return ( <> <div className="relative flex justify-center my-10 mx-4"> <img src="/sphere_3D.jpg" alt="sph" width="600" /> <p className="huit absolute">8</p> <input className="dots top-40" value="/piste1.mp3" onClick={changeTrack} /> <input className="dots top-20 left-2/3" value="/piste2.mp3" onClick={changeTrack} /> </div> <div> <audio ref={audioPlayer} src={currentTrack} preload="metadata" onCanPlay={(e) => e.target.play()} /> <button className="mx-5" onClick={togglePlayPause}> {isPlaying ? "Pause" : "Play"} </button> {/* Current time */} <div>{calculateTime(currentTime)}</div> {/* progress bar */} <div> <input type="range" defaultValue="0" ref={progressBar} onChange={changeRange} /> </div> {/* duration */} <div>{duration && !isNaN(duration) && calculateTime(duration)}</div> </div> </> ); }; export default AudioPlayer;
内容的提问来源于stack exchange,提问作者Rom-888
相关产品推荐
相关产品推荐

