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
这是我用来在我的反应应用中用url(this.url)播放声音的代码。当我按下播放按钮时,它会给我一个错误
Uncaught TypeError: Cannot read property 'setState' of undefined
我不确定为什么这是开心的,因为我没有看到任何未定义的状态。一个;;国家已经宣布。
我是新手,所以我可能会遗漏一些非常重要的事情。
请帮忙!
答案 0 :(得分:19)
您需要在构造函数中绑定play
和pause
方法,以便在这些方法中使用的this
引用组件的实例。
在退出构造函数之前添加这两行:
this.play = this.play.bind(this);
this.pause = this.pause.bind(this);
此外,但这不是你的问题,你可以真正缩短你的代码,那里有很多冗余。类似的东西:
编辑:已更新为使用ES6类属性语法
class Music extends React.Component {
state = {
play: false
}
audio = new Audio(this.props.url)
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;
Hooks版本(React 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]
);
return [playing, toggle];
};
const Player = ({ url }) => {
const [playing, toggle] = useAudio(url);
return (
<div>
<button onClick={toggle}>{playing ? "Pause" : "Play"}</button>
</div>
);
};
export default Player;
答案 1 :(得分:1)
在使用 Next Js 时,我在执行这些步骤时遇到了一些问题,因为 Audio 是 HTMLElement 标签,最终,它给我带来了一个很大的错误,所以我决定研究更多,在我的项目中结果如下:
//inside your component function.
const [audio] = useState( typeof Audio !== "undefined" && new Audio("your-url.mp3")); //this will prevent rendering errors on NextJS since NodeJs doesn't recognise HTML tags neither its libs.
const [isPlaying, setIsPlaying] = useState(false);
为了处理播放器,我做了一个useEffect:
useEffect(() => {
isPlaying ? audio.play() : audio.pause();
}, [isPlaying]);
您将根据您目前所做的功能管理状态“isPlaying”。
答案 2 :(得分:0)
未捕获的TypeError:无法读取未定义的属性'setState'
由于this关键字在JavaScript中的工作方式而发生错误。我认为,如果我们解决该问题,音频应该可以正常播放。
如果您在console.log(this)
内执行play()
,则会看到this
是未定义的,这就是为什么它会引发该错误的原因,因为您正在执行this.setState()
。 this
中play()
的值取决于该函数的调用方式。
React有两种常见的解决方案:
constructor(props) {
super(props);
this.play() = this.play.bind(this);
}
<button onClick={() => {this.play()}}>Play</button>
现在,您将可以访问this.setState
内的this.audio
和play()
,并且pause()
也是如此。
答案 3 :(得分:0)
我在这里参加聚会有点晚了,但是小伙子放弃了'Thomas Hennes':
人们会遇到的一个问题是,如果您尝试在具有多个页面的应用程序中逐字使用此代码,他们将不会过得很愉快。由于状态是在组件上管理的,因此您可以播放,导航和再次播放。
要解决这个问题,您想让组件将其状态推送到App.js并在那里管理状态。
允许我显示我的意思。
我的播放器组件如下:
import React, { Component } from 'react'
class MusicPlayer extends Component {
render() {
const { playing } = this.props.player;
return (
<div>
<button onClick={this.props.toggleMusic.bind(this, playing)}>{playing ? "Pause" : "Play"}</button>
</div>
);
}
};
export default MusicPlayer;
然后在我的App.js中,它看起来像这样(使用TODO列表示例应用程序):
import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom'
import './App.css';
import Header from './componets/layout/Header'
import Todos from './componets/Todos'
import AddTodo from './componets/AddTodo'
import About from './componets/pages/About'
import MusicPlayer from './componets/MusicPlayer'
import axios from 'axios';
class App extends Component {
constructor(props) {
super(props);
this.state = { playing: false, todos: [] }
this.audio = new Audio('<YOUR MP3 LINK HERE>');
}
componentDidMount(){
axios.get('https://jsonplaceholder.typicode.com/todos')
.then(res => this.setState({ playing: this.state.playing, todos: res.data }))
}
toggleComplete = (id) => {
this.setState({ playing: this.state.playing, todos: this.state.todos.map(todo => {
if (todo.id === id){
todo.completed = !todo.completed
}
return todo
}) });
}
delTodo = (id) => {
axios.delete(`https://jsonplaceholder.typicode.com/todos/${id}`)
.then(res => this.setState({ playing: this.state.playing, todos: [...this.state.todos.filter(todo => todo.id !== id)] }));
}
addTodo = (title) => {
axios.post('https://jsonplaceholder.typicode.com/todos', {
title,
completed: false
})
.then(res => this.setState({ playing: this.state.playing, todos: [...this.state.todos, res.data]}))
}
toggleMusic = () => {
this.setState({ playing: !this.state.playing, todos: this.state.todos}, () => {
this.state.playing ? this.audio.play() : this.audio.pause();
});
}
render() {
return (
<Router>
<div className="App">
<div className="container">
<Header />
<Route exact path="/" render={props => (
<React.Fragment>
<AddTodo addTodo={this.addTodo} />
<Todos todos={this.state.todos} toggleComplete={this.toggleComplete} delTodo={this.delTodo} />
</React.Fragment>
)} />
<Route path="/About" render={props => (
<React.Fragment>
<About />
<MusicPlayer player={this.state} toggleMusic={this.toggleMusic} />
</React.Fragment>
)} />
</div>
</div>
</Router>
);
}
}
export default App;
答案 4 :(得分:0)
我在这个答案的实现中遇到了一个不同的问题。
似乎浏览器一直在尝试在每次重新渲染时下载声音。
我最终将 useMemo
用于没有依赖关系的音频,这导致钩子只创建一次音频并且从不尝试重新创建它。
import {useMemo, useEffect, useState} from "react";
const useAudio = url => {
const audio = useMemo(() => 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];
};
export default useAudio;