7

Spotify has a new feature in beta supporting full song playback in browser, Web Playback SDK. The documentation shows an example of immediately initializing a player using script tags in the main HTML file. This requires an access token to be set immediately in the script. My issue is that I am creating a React app and I only want to initialize a player if a user clicks a button to login to their Spotify account. In order to load in a script only once this event occurs, I am using react-load-script. The flow I want is: a user clicks a button to login to their Spotify account, once their login is authenticated by Spotify my app receives an access token, the web playback script is then loaded, once the script is loaded a callback function is called and the player is initialized using the access token.

The problem is that the Spotify object does not exist until the spotify-player script has been loaded in. When the handleScriptLoad callback is actually invoked, the Spotify object is defined, but when the code is being compiled, it is not. Does anyone have any ideas how to get around this problem?

Code sample from the relevant React component:

import Script from 'react-load-script'

...

handleScriptLoad = () => {
const token = this.props.tokens.access
const player = new Spotify.Player({      // Spotify is not defined until 
  name: 'Spotify Web Player',            // the script is loaded in 
  getOAuthToken: cb => { cb(token) }
})

player.connect()
}

...

in my React component's render method:

<Script 
  url="https://sdk.scdn.co/spotify-player.js" 
  onError={this.handleScriptError} 
  onLoad={this.handleScriptLoad}
/>
bitwitch
  • 467
  • 1
  • 5
  • 15
  • Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Liam Dec 04 '17 at 17:09

1 Answers1

4

In theory, it is possible.

The Web Playback SDK will asynchronously load the window.Spotify object in our SDK via an external script. So, waiting for the object to be defined should solve the problem:

handleScriptLoad = () => {
  return new Promise(resolve => {
    if (window.Spotify) {
      resolve();
    } else {
      window.onSpotifyWebPlaybackSDKReady = resolve;
    }
  });
}

That should solve your problem.

Bilawal Hameed
  • 258
  • 4
  • 11
  • 1
    Thanks Bilawal Hameed! I actually solved the issue simply by defining the player as window.Spotify.Player instead of Spotify.Player. This effectively ignores that window.Spotify is undefined until the script is loaded the handleScriptLoad function is run. – bitwitch Dec 14 '17 at 21:08
  • Interesting. Wouldn't that throw an error as `window.Spotify` would be `undefined` and you're trying to call a method on it? – Bilawal Hameed Dec 21 '17 at 16:36
  • It doesn't throw an error. Apparently the `Player` method is not getting checked at compile time, so it doesn't matter that `Spotify` is initially `undefined`. – bitwitch Dec 25 '17 at 05:24
  • The SDK defines `window.Spotify.Player`, so whenever we call `Spotify.Player` it will attempt to look for it locally and globally. And once the SDK loads, it will be defined globally. The two should be synonymous, unless you've overridden it locally. – Bilawal Hameed Jan 01 '18 at 13:37
  • I agree, but for some reason it fails to compile when it is defined as `Spotify.Player` but it works fine when it is defined as `window.Spotify.Player`. My only guess was that at compilation time it is not checking that Player is actually a method on Spotify, only that Spotify is a valid object. In any case, I ended up using the solution that you presented as it is more robust and I actually understand it, thanks for your help. – bitwitch Jan 09 '18 at 01:32
  • That makes sense! Usually it's a good idea to just specify `window.Spotify.Player` like you have done. Glad that you found it useful! Feel free to reach out if you have any other questions, I work at Spotify :) – Bilawal Hameed Jan 09 '18 at 09:36