skip to Main Content

I have playerInstance variable on which I can call methods like togglePlay(). I wanna be able to call it’s methods from different components. I tried to do this which Context API but it only works for sharing properties, when I’m trying to call spotifyPlayer.togglePlay() it shows an error: spotifyPlayer.togglePlay is not a function. I guess that useState() only stores properties but then how am I supposed to share methods? Is even using Context API any good for this problem? I already use redux-toolkit for sharing data

SpotifyPlayerContext.jsx

import React, { createContext, useEffect, 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()

    useEffect(() => {
        console.log(spotifyPlayer)
    }, [spotifyPlayer])

    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 })
                console.log("instance", 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={() => {console.log("spotifyPlayer", spotifyPlayer);spotifyPlayer.togglePlay()}}>toggle</button>
    ) : (
        <div>loading</div>
    )
}

export default Player

2

Answers


  1. Chosen as BEST ANSWER

    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

    import React, { createContext, useEffect, useState } from "react"
    import { useSelector } from "react-redux"
    
    export const SpotifyPlayerContext = createContext()
    
    let playerInstance; // create variable
    
    export const SpotifyPlayerProvider = ({ children }) => {
        const user = useSelector(state => state.user.value)
        const [spotifyPlayer, setSpotifyPlayer] = useState()
    
        useEffect(() => {
            console.log(spotifyPlayer)
        }, [spotifyPlayer])
    
        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 = () => { // only assign
                playerInstance = new window.Spotify.Player({
                    name: "Figure Out The Lyrics",
                    getOAuthToken: (cb) => {
                        cb(user.token)
                    },
                    volume: 0.1
                })
    
                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 })
                    console.log("instance", playerInstance)
                })
            }
        }
    
        const value = { spotifyPlayer, setSpotifyPlayer, initSpotifyPlayer, playerInstance } // export playerInstance
    
        return <SpotifyPlayerContext.Provider value={value}> {children} </SpotifyPlayerContext.Provider>
    }
    
    export default SpotifyPlayerContext
    

    Updated 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, playerInstance } = useContext(SpotifyPlayerContext) // import playerInstance
    
        useEffect(() => {
            if (!spotifyPlayer) return
            if (!spotifyPlayer.playback) spotifyAPI.transferMyPlayback([spotifyPlayer.device_id])
            console.log("useEffect in Player", spotifyPlayer)
        }, [spotifyPlayer])
    
        return spotifyPlayer?.playback && playerInstance ? (
            <button onClick={() => playerInstance.togglePlay()}>toggle</button> // call it's methods
        ) : (
            <div>loading</div>
        )
    }
    
    export default Player
    

  2. Why don’t you try creating method to call togglePlay in Provider and use it in your component? Doesn’t it work?

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search