I’m creating a React (functional components) app which is based on Spotify Web API. I have no problems with this API but i have problems with Spotify Web Playback SDK. It’s a library which allows browser to control music player. As far as i know it’s the only way I can play Spotify music in my app. So i tried to implement it with their official tutorial. But it’s based on the singular object Player and it has methods such as setVolume, togglePlay or nextTrack. I want to be able to access this methods in different components. And my question is how to share this one object with it’s methods? I use redux toolkit for sharing data but i suppose I can’t share complex objects like this with it or can I?
I thought about creating Player object in a root and pass it with props but then I would have to useState for it in every component to show data from it and the object wouldn’t be in sync. Am I misunderstanding something or it’s a wrong way of doing it?
I don’t need this object overall. What I need is data from it and to be able to call it’s methods in components.
Here is a tutorial that I was following but it has no nested components.
Here is a reference to the Player object that I want to use.
UPDATE
I tried to solve it using Context API.
SpotifyPlayerContext.jsx
import React, { createContext, useState } from 'react'
import { useSelector } from 'react-redux'
export const SpotifyPlayerContext = createContext()
export const SpotifyPlayerProvider = ({ children }) => {
const user = useSelector(state => state.user.value)
const [spotifyPlayer, setSpotifyPlayer] = useState()
const initSpotifyPlayer = () => {
const script = document.createElement("script")
script.src = "https://sdk.scdn.co/spotify-player.js"
script.async = true
document.body.appendChild(script)
window.onSpotifyWebPlaybackSDKReady = () => {
const playerInstance = new window.Spotify.Player({
name: "Figure Out The Lyrics",
getOAuthToken: (cb) => { cb(user.token) }
})
playerInstance.connect()
playerInstance.addListener("ready", ({ device_id }) => {
console.log("Device ID", device_id)
playerInstance.device_id = device_id
setSpotifyPlayer({...playerInstance})
})
playerInstance.addListener("player_state_changed", state => {
console.log('player state changed', state);
playerInstance.playback = state;
setSpotifyPlayer({...playerInstance})
})
}
}
const value = { spotifyPlayer, setSpotifyPlayer, initSpotifyPlayer }
return <SpotifyPlayerContext.Provider value={value}> {children} </SpotifyPlayerContext.Provider>
}
export default SpotifyPlayerContext
Player.jsx
import React, { useContext, useEffect, useState } from "react"
import { useDispatch, useSelector } from "react-redux"
import { Tooltip } from "react-tooltip"
import SpotifyPlayerContext from "../SpotifyPlayerContext"
const Player = ({ spotifyAPI }) => {
const { spotifyPlayer, setSpotifyPlayer } = useContext(SpotifyPlayerContext)
useEffect(() => {
if (!spotifyPlayer) return
if (!spotifyPlayer.playback) spotifyAPI.transferMyPlayback([spotifyPlayer.device_id])
console.log("useEffect in Player", spotifyPlayer)
}, [spotifyPlayer])
return spotifyPlayer?.playback ? (
<button onClick={() => spotifyPlayer.togglePlay()}>toggle</button>
) : (
<div>loading</div>
)
}
export default Player
I’m able to get data from it and when the spotifyPlayer changes the component is re-rendered but when i click the button I get error which says that spotifyPlayer.togglePlay is not a function
So, how am I supposed to access it’s methods? I guess that when I assign playerInstance to spotifyPlayer state it looses it’s methods but how to solve this problem?
2
Answers
Okay, I was able to make it work. Instead of creating playerInstance when the onSpotifyWebPlaybackSDKReady I created it even outside of the component and only assign the player to it. So now I get data from spotifyPlayer but I call methods on playerInstance. Maybe it's not perfect but it looks like it works for now.
Updated SpotifyPlayerContext.jsx
Updated Player.jsx
There is this line in the tutorial:
Instead of keeping this player instance in just one component, save it in a module that your other components can import:
So you would
import { savePlayerInstance } from 'some/path/player'
and callsetPlayer(player)
after the instance is created (the line from your tutorial). Other modules canimport { getPlayer } from 'some/path/player'
, callgetPlayer()
and invoke its methods.