import React, { Component } from 'react' import { Button, Input, Icon,Dropdown,Card} from 'semantic-ui-react' import { Link } from 'react-router-dom' import $ from 'jquery' import styles from './Home.scss' import Modal from './Modal.jsx' import MakeChannelModal from './MakeChannelModal.jsx' class Music extends React.Component { constructor(props) { super(props); this.state = { play: false, pause: true }; this.url = "http://streaming.tdiradio.com:8000/house.mp3"; this.audio = new Audio(this.url); } play(){ this.setState({ play: true, pause: false }); console.log(this.audio); this.audio.play(); } pause(){ this.setState({ play: false, pause: true }); this.audio.pause(); } render() { return ( <div> <button onClick={this.play}>Play</button> <button onClick={this.pause}>Pause</button> </div> ); } } export default Music
这是我用来在我的react应用程序中使用url(this.url)播放声音的代码。当我按下播放按钮时,它给我一个错误
Uncaught TypeError: Cannot read property 'setState' of undefined
我不确定为什么会这样,因为我没有看到任何未定义的状态。一个;; 状态已经声明。
我是新来的反应者,所以我可能会错过一些非常重要的东西。
请帮忙!
我稍微改进了Jaxx的版本,使其包含一个,eventListener以便在音频结束时重置按钮。
eventListener
ES6类属性语法
class Music extends React.Component { state = { play: false } audio = new Audio(this.props.url) componentDidMount() { audio.addEventListener('ended', () => this.setState({ play: false })); } componentWillUnmount() { audio.removeEventListener('ended', () => this.setState({ play: false })); } togglePlay = () => { this.setState({ play: !this.state.play }, () => { this.state.play ? this.audio.play() : this.audio.pause(); }); } render() { return ( <div> <button onClick={this.togglePlay}>{this.state.play ? 'Pause' : 'Play'}</button> </div> ); } } export default Music;
挂钩版本(反应16.8+):
import React, { useState, useEffect } from "react"; const useAudio = url => { const [audio] = 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]; }; const Player = ({ url }) => { const [playing, toggle] = useAudio(url); return ( <div> <button onClick={toggle}>{playing ? "Pause" : "Play"}</button> </div> ); }; export default Player;
更新03/16/2020:多个并发玩家
回应@Cold_Class的评论:
不幸的是,如果我使用这些组件中的多个组件,则当我开始播放另一个组件时,来自其他组件的音乐不会停止播放-关于此问题的简单解决方案有何建议?
不幸的是,没有使用我们用来实现单个Player组件的确切代码库的简单解决方案。原因是您必须以某种方式将单个播放器状态提升到MultiPlayer父组件,以便该toggle功能能够暂停与您直接交互的播放器以外的其他播放器。
Player
MultiPlayer
toggle
一种解决方案是修改挂钩本身,以同时管理多个音频源。这是一个示例实现:
import React, { useState, useEffect } from 'react' const useMultiAudio = urls => { const [sources] = useState( urls.map(url => { return { url, audio: new Audio(url), } }), ) const [players, setPlayers] = useState( urls.map(url => { return { url, playing: false, } }), ) const toggle = targetIndex => () => { const newPlayers = [...players] const currentIndex = players.findIndex(p => p.playing === true) if (currentIndex !== -1 && currentIndex !== targetIndex) { newPlayers[currentIndex].playing = false newPlayers[targetIndex].playing = true } else if (currentIndex !== -1) { newPlayers[targetIndex].playing = false } else { newPlayers[targetIndex].playing = true } setPlayers(newPlayers) } useEffect(() => { sources.forEach((source, i) => { players[i].playing ? source.audio.play() : source.audio.pause() }) }, [sources, players]) useEffect(() => { sources.forEach((source, i) => { source.audio.addEventListener('ended', () => { const newPlayers = [...players] newPlayers[i].playing = false setPlayers(newPlayers) }) }) return () => { sources.forEach((source, i) => { source.audio.removeEventListener('ended', () => { const newPlayers = [...players] newPlayers[i].playing = false setPlayers(newPlayers) }) }) } }, []) return [players, toggle] } const MultiPlayer = ({ urls }) => { const [players, toggle] = useMultiAudio(urls) return ( <div> {players.map((player, i) => ( <Player key={i} player={player} toggle={toggle(i)} /> ))} </div> ) } const Player = ({ player, toggle }) => ( <div> <p>Stream URL: {player.url}</p> <button onClick={toggle}>{player.playing ? 'Pause' : 'Play'}</button> </div> ) export default MultiPlayer
App.js使用MultiPlayer组件的示例:
App.js
import React from 'react' import './App.css' import MultiPlayer from './MultiPlayer' function App() { return ( <div className="App"> <MultiPlayer urls={[ 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3', 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-2.mp3', 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-3.mp3', ]} /> </div> ) } export default App
这个想法是管理2个并行数组:
urls
该toggle方法根据以下逻辑更新播放器状态数组:
注意,该toggle方法被强制接受源播放器的索引(即,单击相应按钮的子组件的索引)。
实际的音频对象控制useEffect与原始钩子一样发生,但是稍微复杂一些,因为我们每次更新都必须遍历整个音频对象数组。
useEffect
类似地,音频流“结束”事件的事件侦听器将在第二秒内进行处理,useEffect就像在原始钩子中一样,但是会更新以处理音频对象数组,而不是单个此类对象。
最后,从上级MultiPlayer组件(容纳多个播放器)中调用新的钩子,然后Player使用(a)一个包含播放器当前状态及其源流URL的对象,以及(b)使用玩家指数。
CodeSandbox演示