In React (Next.js) with hooks using typescript, I want to create a reference to Audio. The error I'm getting is:-
Property 'duration' does not exist on type 'false | HTMLAudioElement'.
Property 'duration' does not exist on type 'false'.ts(2339)
const audioRef = useRef(typeof Audio !== 'undefined' && new Audio(mp3Src));
const { duration } = audioRef.current.duration;
I've also tried like this
const audioRef = useRef(new Audio(mp3Src));
const { duration } = audioRef.current;
which then throws an error in the browser
Server Error
ReferenceError: Audio is not defined
This error happened while generating the page. Any console logs will be displayed in the terminal window.
Complete code
import type { NextPage } from 'next';
import React, { useRef, useState } from 'react';
import Image from 'next/image';
interface Props {
imgSrc: string;
mp3Src: string;
}
const AudioPlayer: NextPage<Props> = ({ imgSrc, mp3Src }) => {
// const audioRef = useRef(new Audio(mp3Src));
const audioRef = useRef(typeof Audio !== 'undefined' && new Audio(mp3Src));
// const duration = audioRef.current.duration;
const { duration } = audioRef.current;
const intervalRef = useRef<any>();
const [trackProgress, setTrackProgress] = useState<number>(0);
const [isPlaying, setIsPlaying] = useState<boolean>(false);
const play = (): void => {
audioRef.current.play();
startTimer();
setIsPlaying(true);
};
const pause = (): void => {
audioRef.current.pause();
setIsPlaying(false);
};
const startTimer = (): void => {
clearInterval(intervalRef.current);
intervalRef.current = setInterval(() => {
setTrackProgress(audioRef.current.currentTime);
}, 100);
};
const onScrub = (value: number): void => {
// Clear any timers already running
clearInterval(intervalRef.current);
audioRef.current.currentTime = value;
setTrackProgress(audioRef.current.currentTime);
};
const onScrubEnd = (): void => {
if (!isPlaying) {
setIsPlaying(true);
}
startTimer();
};
const mute = (): void => {
audioRef.current.volume = 0;
};
return (
<div className='AudioPlayer'>
<Image alt='menu' src={imgSrc} width='30' height='20' />
<div onClick={play}>Play</div>
<div onClick={pause}>Pause</div>
<div>{trackProgress}</div>
<div>{isPlaying}</div>
<input
type='range'
value={trackProgress}
step='1'
min='0'
max={duration ? duration : `${duration}`}
className='progress'
onChange={(e): void => onScrub( e.target.value)}
onm ouseUp={onScrubEnd}
onKeyUp={onScrubEnd}
/>
<div onClick={mute}>Mute</div>
</div>
);
};
export default AudioPlayer;
CodePudding user response:
NextJS uses Node to render anything in the server that allows you to build static and server side rendered apps. Therefore, any browser's APIs such as window or in your case Audio, do not exist in Node.
When Next tries to run the following code on node, and since Audio does not exist. It will evaluate to false since typeof Audio is undefined.
const audioRef = useRef(typeof Audio !== 'undefined' && new Audio(mp3Src));
To ensure the code is run only on browsers (not Node), you can use the useEffect hook.
e.g.
const AudioPlayer: NextPage<Props> = ({ imgSrc, mp3Src }) => {
// const audioRef = useRef(new Audio(mp3Src));
const [audioElem, setAudioElem] = useState<HTMLAudioElement>();
useEffect(() => {
if (typeof Audio !== 'undefined' && mp3Src) {
const audio = new Audio(mp3Src);
setAudioElem(audio);
}
}, [mp3Src])
// const duration = audioRef.current.duration;
const { duration } = audioElem || {};
...
Some helpful links;
- https://dev.to/vvo/how-to-solve-window-is-not-defined-errors-in-react-and-next-js-5f97
- https://frontend-digest.com/why-is-window-not-defined-in-nextjs-44daf7b4604e
CodePudding user response:
There is a possibility that audioRef.current could be false in your code.
Check for that before your read duration:
const audioRef = useRef(typeof Audio !== 'undefined' && new Audio(mp3Src));
if (audioRef.current) {
const { duration } = audioRef.current;
}
