首页 > 解决方案 > 一次只播放一首歌曲 (React)

问题描述

我正在尝试通过 API 创建一个音乐应用程序,在其中我为每首歌曲渲染一个歌曲组件,每首歌曲都有自己的播放、暂停和停止按钮,但是当我在播放歌曲时播放另一首歌曲时,我想要前一首将停止播放。就像其他音乐应用程序一样。

这是处理播放、暂停和停止的组件

const useAudio = (song_url) => {
  const audio = useRef(new Audio(song_url));
  audio.current.preload = "metadata";

  const [isPlaying, setPlaying] = useState(false);

  const toggleAudio = () => {
    setPlaying(!isPlaying);
  };

  const handleStop = () => {
    audio.current.pause();
    audio.current.currentTime = 0;
    setPlaying(false);
  };

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

  useEffect(() => {
    audio.current.addEventListener("ended", () => setPlaying(false));

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

  return [isPlaying, toggleAudio, handleStop];
};

标签: javascriptreactjsreact-hooks

解决方案


每次调用 useAudio 时,您都在创建具有独立状态的钩子的新实例,因此您无法控制从一个钩子到另一个钩子。

为了用一个钩子控制所有歌曲,您可能应该创建一个商店。

这是我将要做的一个简短示例。请根据您的需要进行必要的更改。

//We first create a store
export const AudioContext = createContext();
export const useAudio = () => {
    const context = useContext(AudioContext);
    if (!context && typeof window !== 'undefined') {
        throw new Error(`useAudio must be used within a AudioContext `);
    }
    return context;
};

//Then we create the provider
export const AudioProvider = ({ children }) => {

    const [ song, _setSong ] = useState()

    const setSong = (url) => {
          song.pause();
          const newSong = new Audio(url)
          newSong.play()
          setSong(newSong)
    }
    const pauseSong = () => song.pause()

    
    return <AudioContext.Provider value={{ setSong, pauseSong }}>{children}</AudioContext.Provider>
}

然后你应该用<AudioProvider>

用法:

const { setSong, pauseSong } = useAudio()

const songSelected = (url) => setSong(url)

setSong会先暂停原曲,然后用新的url创建一个新的Audio对象,然后播放。

一次只能播放一首歌曲。


推荐阅读