I am trying to switch between songs in next js using the use effect hook but everything works perfectly when I click the button the song start automatically and the song switch but the color behind the progress bar does match the current time because it getting the progress of the first song that is loaded in the browser how can I fix this using the use Effect or other method?
if you noticed is does not load properly at the first time when you click the button to switch the song but when you click the play / pause button it fixed I don't know why.
Also it give me the last value not the actual value I mean I need for example the 140 but it always gave me the 247 value the prev Value
post: is the part // ! useEffect to change the audio
import React, { useState, useRef, useEffect } from "react";
import styles from "../styles/AudioPlayer.module.css";
import { BiLeftArrowAlt } from "react-icons/bi";
import { BiRightArrowAlt } from "react-icons/bi";
import { FaPlay } from "react-icons/fa";
import { FaPause } from "react-icons/fa";
const AudioPlayer = () => {
// ! state
const [isPlaying, setIsPlaying] = useState(false);
const [duration, setDuration] = useState(0);
const [currentTime, setCurrentTime] = useState(0);
const [audio, setAudio] = useState(null);
const chapters = [
{
// start: 0,
// end: 40,
start: 0,
end: 8,
},
{
start: 135,
end: 140,
// start: 100,
// end: 140,
},
];
const Elvis = "../audios/AudioElvis.mp3";
const GetLucky = "../audios/GetLucky.mp3";
// ! refs
const audioPlayer = useRef(); // ? reference to the audio component
const progressBar = useRef(); // ? reference to the progress bar
const animationRef = useRef(); // ? reference to the animation
// ! useEffect to handle the audio player
useEffect(() => {
const seconds = Math.floor(audioPlayer.current.duration);
setDuration(seconds);
// ! current is a reference to the current item
// ? max is the reference given by the progress bar
progressBar.current.max = seconds;
}, [audioPlayer?.current?.loadedmetadata, audioPlayer?.current?.readyState]);
// ! useEffect to handle the end of the audio
useEffect(() => {
if (currentTime == duration && isPlaying) {
togglePlayPause();
timeTravel(0);
}
}, [currentTime]);
// ! useEffect to change the audio
useEffect(() => {
if (audio && !isPlaying) {
if (audio === Elvis) {
setDuration(140);
} else if (audio === GetLucky) {
setDuration(247);
}
console.log("audio", audio);
console.log("duration", duration);
setDuration(audioPlayer.current.duration);
audioPlayer.current.src = audio;
audioPlayer.current.load(); // ? load the audio
togglePlayPause();
timeTravel(0);
} else if (audio && isPlaying) {
if (audio === Elvis) {
setDuration(140);
} else if (audio === GetLucky) {
setDuration(247);
}
console.log("audio", audio);
console.log(audio === Elvis);
console.log(audio === GetLucky);
console.log("duration", duration);
audioPlayer.current.src = audio;
audioPlayer.current.load(); // ? load the audio
audioPlayer.current.play();
timeTravel(0);
}
}, [audio]);
// ! FUNCTIONS
const calculateTime = (secs) => {
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 togglePlayPause = () => {
const prevValue = isPlaying;
setIsPlaying(!prevValue);
if (!prevValue) {
audioPlayer.current.play();
// ? requestAnimationFrame is a function that allows us to run a function
// ? every frame of the animation
animationRef.current = requestAnimationFrame(whilePlaying);
} else {
audioPlayer.current.pause();
cancelAnimationFrame(animationRef.current);
}
};
// ! Creating our animation function => whilePlaying
const whilePlaying = () => {
progressBar.current.value = audioPlayer.current.currentTime;
changePlayerCurrentTime();
animationRef.current = requestAnimationFrame(whilePlaying);
};
// ! Changing the range of the progress bar FUNCTION
const changeRange = () => {
// ! Setting our audio current time to the value of the progress bar
audioPlayer.current.currentTime = progressBar.current.value;
changePlayerCurrentTime();
};
const changePlayerCurrentTime = () => {
// ! Updating the progress bar in CSS
// ? progressBar.current.style.setProperty => takes two arguments
// ? first is the property name
// ? second is the value of the property
// console.log("bar", progressBar.current.value);
// console.log("duration", duration);
progressBar.current.style.setProperty(
"--seek-before-width",
`${(progressBar.current.value / duration) * 100}%`
);
setCurrentTime(progressBar.current.value);
};
const backThirty = () => {
progressBar.current.value = Number(progressBar.current.value) - 30;
changeRange();
};
const forwardThirty = () => {
progressBar.current.value = Number(progressBar.current.value) + 30;
changeRange();
};
const timeTravel = (newTime) => {
progressBar.current.value = newTime;
changeRange();
};
// // ! Changing the audio source
// const changeSong = (song) => {
// setAudio(song);
// setIsPlaying(false);
// timeTravel(0);
// };
return (
<div className={styles.audioPlayer}>
{/* Audio Player Reference */}
<audio
ref={audioPlayer}
// src="https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
src={Elvis}
preload="metadata"
></audio>
{/* End of the Audio Player Reference */}
{/* Current Time */}
<div className={styles.currentTime}>{calculateTime(currentTime)}</div>
{/* Progress Bar */}
<div className={styles.progressBarWrapper}>
<input
type="range"
className={styles.progressBar}
defaultValue="0"
ref={progressBar}
onChange={changeRange}
step="0.05"
/>
{/* Chapters Video 2 */}
{chapters.map((chapter, i) => {
const leftStyle = (chapter.start / duration) * 100;
const widthStyle = ((chapter.end - chapter.start) / duration) * 100;
return (
<div
key={i}
className={`${styles.chapter} ${
chapter.start === 0 && styles.start
} ${chapter.end === duration && styles.end}`}
style={{
"--left": `${leftStyle}%`,
"--width": `${widthStyle}%`,
}}
></div>
);
})}
</div>
{/* Duration */}
<div className={styles.duration}>
{duration && !isNaN(duration) ? calculateTime(duration) : "0:00"}
</div>
{/* Controls */}
<div className={styles.controls}>
<button className={styles.forwardBackward} onClick={backThirty}>
<BiLeftArrowAlt /> 30
</button>
<button onClick={togglePlayPause} className={styles.playPause}>
{isPlaying ? <FaPause /> : <FaPlay className={styles.play} />}
</button>
<button className={styles.forwardBackward} onClick={forwardThirty}>
30 <BiRightArrowAlt />
</button>
</div>
<div>
<button onClick={() => setAudio(Elvis)}>Elvis Song</button>
<button onClick={() => setAudio(GetLucky)}>Get Lucky Song</button>
</div>
</div>
);
};
export { AudioPlayer };
// ! the {} means that this is a named export