skip to Main Content

enter image description here

I have this application of a word bank for students and it can play the audio for the words coded like this:

import { IconButton } from "@chakra-ui/button";
import { useEffect, useState } from "react";
import { HiSpeakerWave } from "react-icons/hi2";

const useAudio = (url: string) => {
  const  = useState(new Audio(url));
  const [playing, setPlaying] = useState(false);

  const toggle = () => setPlaying(!playing);

  useEffect(() => {
    playing ? audio.play() : audio.pause();
  }, [playing]);

  useEffect(() => {
    audio.addEventListener("ended", () => setPlaying(false));
    return () => {
      audio.removeEventListener("ended", () => setPlaying(false));
    };
  }, []);

  return [playing, toggle];
};

interface Prop {
  url: string;
}

const Player = ({ url }: Prop) => {
  const [playing, toggle] = useAudio(url);

  return (
    <IconButton
      size={"sm"}
      colorScheme="teal"
      aria-label="Play Audio"
      borderRadius={"full"}
      icon={<HiSpeakerWave />}
      onClick={toggle}
    />
  );
};

export default Player;

I don’t want all the audio to be fetched because the user will never play them all in one session. What changes do I need to make to this to make it lazy? For it to only load when a user clicks play. I think of someone way to wrap the component inside of a click and then return a component that loads and plays the actual audio.

2

Answers


  1. Chosen as BEST ANSWER

    I thought of this approach where I set the URL for the audio through a state. This doesn't give me errors and plays the audio.

    const useAudio = (url: string) => {
      const [audio, setAudio] = useState(new Audio(url));
      const [playing, setPlaying] = useState(false);
    
      const toggle = () => setPlaying(!playing);
    
      useEffect(() => {
        if (url.length) {
          setAudio(new Audio(url));
        }
      }, [url]);
    
      useEffect(() => {
        if (url) {
          audio.play();
        }
      }, [audio]);
    
      useEffect(() => {
        playing ? audio.play() : audio.pause();
      }, [playing]);
    
      useEffect(() => {
        audio.addEventListener("ended", () => setPlaying(false));
        return () => {
          audio.removeEventListener("ended", () => setPlaying(false));
        };
      }, []);
    
      return [playing, toggle];
    };
    
    const Player = ({ url }: Prop) => {
      const [_url, setUrl] = useState("");
      const [playing, toggle] = useAudio(_url);
    
      const handleClick = () => {
        if (_url === url) {
          toggle();
        } else {
          setUrl(url);
        }
      };
    
      return (
        <IconButton
          size={"sm"}
          colorScheme="teal"
          aria-label="Play Audio"
          borderRadius={"full"}
          icon={<HiSpeakerWave />}
          onClick={handleClick}
        />
      );
    };
    
    
    

  2. I have done the same lazy loading (audio player) for one of my clients.
    Let me give suggestion in short which will help you (If not do let me know) – Use Redux and dispatch the action on click of every play button.

    Note: action should take an arg of url.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search