在reactjs中播放声音

时间:2017-12-07 01:51:57

标签: javascript reactjs

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

我不确定为什么这是开心的,因为我没有看到任何未定义的状态。一个;;国家已经宣布。

我是新手,所以我可能会遗漏一些非常重要的事情。

请帮忙!

5 个答案:

答案 0 :(得分:19)

您需要在构造函数中绑定playpause方法,以便在这些方法中使用的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()thisplay()的值取决于该函数的调用方式。

React有两种常见的解决方案:

  1. 使用bind()来设置函数的this的值,而不管其如何调用:
constructor(props) {
  super(props);
  this.play() = this.play.bind(this);
}
  1. 使用不提供此绑定的箭头功能
<button onClick={() => {this.play()}}>Play</button>

现在,您将可以访问this.setState内的this.audioplay(),并且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;

相关问题