You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React音频组件:如何实现点击下一曲时停止当前音频

解决多音频同时播放的问题

你的问题出在每个SetAudios组件都维护自己的currentAudio状态——因为TranslationText里每段经文都会渲染一个独立的SetAudios,这些组件的状态互相隔离,所以点击新音频时,没法暂停其他组件里正在播放的音频。

解决思路是把音频控制的状态和逻辑提升到父组件TranslationText,让所有音频组件共享同一个当前播放实例,这样就能统一控制暂停和播放。

修改步骤:

1. 改造TranslationText.tsx

在父组件中维护当前播放的音频实例,并提供统一的播放控制方法:

import React, { useEffect, useState } from 'react'
import { useSignedContext } from '../contexts/SignedContext'
import SetAudios from './SetAudios'
import { useParams } from 'react-router'
import Loader from './Loader'

interface SurahProps {
  name: string
  englishName: string
  englishNameTranslation: string
  number: number
  ayahs: {
    number: number
    text: string
    numberInSurah: number
  }[]
}

const TranslationText: React.FC = () => {
  const { surah } = useParams<{ surah: string }>()
  const { translatedText } = useSignedContext()
  const [data, setData] = useState<SurahProps[]>([])
  const [loading, setLoading] = useState<boolean>(false)
  // 把currentAudio状态移到父组件统一管理
  const [currentAudio, setCurrentAudio] = useState<HTMLAudioElement | null>(null)
  const BASE_URL = 'https://api.alquran.cloud/v1/quran'

  useEffect(() => {
    async function fetchScholarData() {
      try {
        setLoading(true)
        if (translatedText) {
          const response = await fetch(`${BASE_URL}/${translatedText}`).then(
            (response) => response.json()
          )
          setData(response.data.surahs)
        }
        setLoading(false)
      } catch (error) {
        console.error('Error fetching scholar data:', error)
        setLoading(false)
      }
    }
    fetchScholarData()
  }, [translatedText])

  // 统一的音频播放控制方法
  const handlePlayAudio = (audioElement: HTMLAudioElement) => {
    if (currentAudio && currentAudio !== audioElement) {
      currentAudio.pause()
      // 可选:将暂停的音频进度重置到开头
      currentAudio.currentTime = 0
    }
    setCurrentAudio(audioElement)
    audioElement.play()
  }

  if (loading) {
    return <Loader />
  }

  if (!translatedText) {
    return <p className="text-3xl">No translated text available 🙅‍♂️</p>
  }
  const filteredData = data.find((surahData) => surahData.englishName === surah)
  return (
    <>
      {filteredData?.ayahs.map((ayah, i) => (
        <div
          key={ayah.number}
          className="bg-white shadow-md rounded-lg overflow-hidden"
        >
          <div className="px-6 py-4 bg-gray-200">
            <div className="flex gap-5">
              <div className="flex flex-col justify-between">
                <p className="text-3xl font-black">{i + 1}</p>
                <p>
                  <i className="fa-solid fa-share text-3xl"></i>
                </p>
                <p>
                  <i className="fa-solid fa-person-praying text-3xl"></i>
                </p>
              </div>
              <div>
                <p className="text-2xl">{ayah.text}</p>
              </div>
            </div>
            {/* 把控制方法传给子组件 */}
            <SetAudios ayahNumber={ayah.number} onPlay={handlePlayAudio} />
          </div>
          <hr className="border-t-4 my-10 border-emerald-700" />
        </div>
      ))}
    </>
  )
}

export default TranslationText

2. 改造SetAudios.tsx

移除子组件内部的状态和控制逻辑,改用父组件传来的回调:

import React, { useEffect, useState } from 'react'
import { useSignedContext } from '../contexts/SignedContext'
import { useParams } from 'react-router'

interface SurahProps {
  name: string
  englishName: string
  englishNameTranslation: string
  number: number
  ayahs: {
    number: number
    text: string
    numberInSurah: number
    audio: string
  }[]
}
interface SetAudiosProps {
  ayahNumber: number
  // 接收父组件的播放回调
  onPlay: (audioElement: HTMLAudioElement) => void
}

const SetAudios: React.FC<SetAudiosProps> = ({ ayahNumber, onPlay }) => {
  const { surah } = useParams<{ surah: string }>()
  const { selectedAudioFormat } = useSignedContext()
  const [data, setData] = useState<SurahProps[]>([])
  const [loading, setLoading] = useState<boolean>(false)
  const BASE_URL = 'https://api.alquran.cloud/v1/quran/'

  useEffect(() => {
    async function fetchScholarData() {
      try {
        setLoading(true)
        if (selectedAudioFormat) {
          const response = await fetch(
            `${BASE_URL}/${selectedAudioFormat}`
          ).then((response) => response.json())
          setData(response.data.surahs)
        }
        setLoading(false)
      } catch (error) {
        console.error('Error fetching scholar data:', error)
        setLoading(false)
      }
    }
    fetchScholarData()
  }, [selectedAudioFormat])

  const filteredData = data.find((surahData) => surahData.englishName === surah)

  if (loading) {
    return <p>Loading...</p>
  }

  if (!selectedAudioFormat) {
    return <p>No translated text available</p>
  }
  return (
    <div className="w-full my-5">
      {filteredData?.ayahs.map((ayah) => {
        if(ayah.number === ayahNumber){
          return (
            <audio
              key={ayah.number}
              className="w-full"
              controls
              // 调用父组件传来的播放方法
              onPlay={(e) => onPlay(e.target as HTMLAudioElement)}
            >
              <source src={ayah.audio} />
            </audio>
          )
        }
      })}
    </div>
  )
}

export default SetAudios

额外优化(可选)

如果希望音频播放完毕后自动清空当前状态,可以在父组件的handlePlayAudio里添加结束监听:

const handlePlayAudio = (audioElement: HTMLAudioElement) => {
  if (currentAudio && currentAudio !== audioElement) {
    currentAudio.pause()
    currentAudio.currentTime = 0
  }
  // 添加播放结束监听
  audioElement.onended = () => {
    setCurrentAudio(null)
  }
  setCurrentAudio(audioElement)
  audioElement.play()
}

内容的提问来源于stack exchange,提问作者Uktam Rakhmatullayev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 08:24:56