react-jPlayer icon indicating copy to clipboard operation
react-jPlayer copied to clipboard

Issue when using multiple players

Open jmvallejo opened this issue 6 years ago • 7 comments

Hi, I'm using react-jPlayer for playing various items inside the same page. Basically I want each player to have its own state and I'm giving it an id based on the element that it will be playing, the first time I load the players it works well, but once I go to a different page where additional players are loaded I get an error from mapStateToProps where it's trying to get media from undefined, as if it was trying to access a player id that doesn't exist from the reducer.

Here's the error: TypeError: Cannot read property 'media' of undefined at Function.mapStateToProps [as mapToProps] (jPlayerContainer.js:36)

image

I'm wrapping the player inside a component that has additional logic for loading the files, but basically I'm initializing with the corresponding id on componentWillMount.

jmvallejo avatar Aug 31 '18 00:08 jmvallejo

Have you had a look at this?

https://github.com/jplayer/react-jPlayer-examples/tree/master/jPlayers/MultiplePlayers

MartinDawson avatar Aug 31 '18 09:08 MartinDawson

Yes! that was useful at first but I'm suspecting my issue is because of the way I'm initializing the players. In the example you provide player states are initialized during import and they have known ids.

By looking at the jplayer code I think that initialization is not happening through a redux action but probably modifying the store directly, I think this may be causing issues when trying to display multiple instances with different ids.

What's strange is that the first time I do it it works, then I move to a different component that does the same and it breaks. I'm still looking at a solution but help is very appreciated :)

jmvallejo avatar Aug 31 '18 13:08 jmvallejo

show your code

On Fri, Aug 31, 2018 at 3:15 PM Juan Manuel Vallejo < [email protected]> wrote:

Yes! that was useful at first but I'm suspecting my issue is because of the way I'm initializing the players. In the example you provide player states are initialized during import and they have known ids.

By looking at the jplayer code I think that initialization is not happening through a redux action but probably modifying the store directly, I think this may be causing issues when trying to display multiple instances with different ids.

What's strange is that the first time I do it it works, then I move to a different component that does the same and it breaks. I'm still looking at a solution but help is very appreciated :)

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/jplayer/react-jPlayer/issues/18#issuecomment-417660233, or mute the thread https://github.com/notifications/unsubscribe-auth/AOVY2zalWk09xDUoxW4YXe6fVgCLq36Xks5uWTbWgaJpZM4WUZ9A .

MartinDawson avatar Aug 31 '18 13:08 MartinDawson

Here's my code for the player:

AudioPlayer (Container):

import React, { Component } from 'react'
import { connect } from 'react-redux'
import AudioPlayerView from '../../../components/content/chat/AudioPlayer'
import { get as getRecording } from '../../../webapi/recordings'
import { getCurrentAccount } from '../../../stateapi/config'
import { actions, initializeOptions } from 'react-jplayer'

export class AudioPlayer extends Component {
  constructor (props) {
    super(props)
    this.state = {
      playerStarted: false,
      playerLoading: false
    }
  }

  componentWillMount () {
    const { id } = this.props
    const playerOptions = {
      id,
      media: {
        sources: {},
        free: true
      }
    }
    initializeOptions(playerOptions)
  }

  componentWillUnmount () {
    const { clearMedia } = this.props
    clearMedia && clearMedia()
  }

  _startPlayer = e => {
    const { playerStarted } = this.state
    if (playerStarted) {
      e && e.preventDefault()
      return
    }
    this.setState(
      {
        playerLoading: true
      },
      () => {
        const { id, apiBaseUrl, setMedia } = this.props
        getRecording(id).then(response => {
          this.setState({
            playerLoading: false,
            playerStarted: true
          })
          if (response.body && response.body.files) {
            const { files } = response.body
            const stateFiles = {}
            for (let fileType in files) {
              stateFiles[fileType] = apiBaseUrl + files[fileType]
            }
            setMedia(stateFiles)
          }
        })
      }
    )
  }

  render () {
    const { id, playerInitialized } = this.props
    const { playerStarted, playerLoading } = this.state
    return (
      <AudioPlayerView
        id={id}
        playerStarted={playerStarted}
        playerLoading={playerLoading}
        startPlayer={this._startPlayer}
        playerInitialized={[playerInitialized]}
      />
    )
  }
}

export function mapStateToProps (state, ownProps) {
  const { id } = ownProps
  const account = getCurrentAccount(state)
  const apiBaseUrl = `https://${account && account.server}:8002/api`
  const { jPlayers } = state
  const playerInitialized = !!(jPlayers[id] &&
    jPlayers[id].mediaSettings &&
    jPlayers[id].media &&
    jPlayers[id].media.sources)

  return {
    apiBaseUrl,
    playerInitialized
  }
}

export function mapDispatchToProps (dispatch, ownProps) {
  const { id } = ownProps
  return {
    setMedia: sources => {
      dispatch(actions.setMedia(id, { sources, free: true }))
      dispatch(actions.play(id))
    },
    clearMedia: () => {
      dispatch(actions.pause(id))
      dispatch(actions.clearMedia(id))
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(AudioPlayer)

Component:

import React from 'react'
import Icon from '../../icons/Icon'
import classNames from 'classnames'
import JPlayer, {
  Gui,
  SeekBar,
  BufferBar,
  Audio,
  Play,
  PlayBar,
  Duration,
  CurrentTime,
  BrowserUnsupported
} from 'react-jplayer'

const AudioPlayer = ({
  id,
  playerStarted,
  startPlayer,
  playerLoading,
  isOld,
  playerInitialized
}) => {
  if (!playerInitialized) {
    return null
  }
  return (
    <JPlayer
      id={id}
      className={classNames('jp-sleek audio-player', { 'is-old': isOld })}
    >
      <Audio />
      <Gui>
        <div className='jp-controls jp-icon-controls' onClick={startPlayer}>
          <Play>
            <Icon name='play' />
            <Icon name='pause' />
          </Play>
          <div className='jp-progress'>
            <SeekBar>
              <BufferBar />
              {playerLoading && <div className='loading' />}
              <PlayBar />
            </SeekBar>
            {playerStarted &&
              <div className='audio-timestamp'>
                <CurrentTime />
                <Duration />
              </div>}
          </div>
        </div>
        <BrowserUnsupported />
      </Gui>
    </JPlayer>
  )
}

export default AudioPlayer

jmvallejo avatar Aug 31 '18 15:08 jmvallejo

I guess JSX won't be properly parsed herre 😞

jmvallejo avatar Aug 31 '18 15:08 jmvallejo

https://github.com/jplayer/react-jPlayer/issues/18#issuecomment-417706417 is it resolved? @jmvallejo

kapilropani avatar Dec 17 '18 08:12 kapilropani

Nope, I stopped trying and implemented my own solution using a native player :(

jmvallejo avatar Dec 17 '18 13:12 jmvallejo