Async function called in a slinky/scalajs setup causes render to fail

Viewed 23

I'm unsure if this is a question for the general scala.js world or specifically slinky/slinky-native/slinky-hot

FYI, using

libraryDependencies += "me.shadaj" %%% "slinky-native" % "0.7.0"
libraryDependencies += "me.shadaj" %%% "slinky-hot" % "0.7.0"

addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.10.1")

I have an async function that returns login status with facebook

object LoginController {
    def userFBLoginStatus: Future[LoginStatus] = {
      FBAccessToken.getCurrentAccessToken.toFuture.map {

This internally calls a static function import from a javascript library

  @native
  @JSImport("react-native-fbsdk-next", JSImport.Default)
  object FBAccessToken extends js.Object {
    def getCurrentAccessToken: Promise[FBAccessToken | Null] = native
  }

I call it in the main App component like so

@react class App extends Component {
  type Props = Unit
  type State = Boolean

  def initialState = false


  override def componentDidMount(): Unit = {
    LoginController.userFBLoginStatus.map {
      case LoggedIn => setState(true)
      case LoggedOut => setState(false)
    }
  }

  override def render(): ReactElement = {
    if (state)
      View(//welcome view)
    else
      View(//login page view)

The intention being that the view should change based on the output of the async function called in componentDidMount sbt clean fastOptJS works fine but on rendering the app fails with

TypeError: undefined is not an object (evaluating 'this$.then')
This error is located at:
    in App$ (at renderApplication.js:50)
    in RCTView (at View.js:32)
    in View (at AppContainer.js:92)
    in RCTView (at View.js:32)
    in View (at AppContainer.js:119)
    in AppContainer (at renderApplication.js:43)
    in DanceApp(RootComponent) (at renderApplication.js:60)
 ERROR  TypeError: undefined is not an object (evaluating 'this$.then')

If I comment out the async call in componentDidMount, the rendering works fine. See the (evaluating 'this$.then'), looks like its looking for a Promise.then?

Equivalent react code works e.g. where fetchWells() returns a javascript Promise

class Map extends Component {
  constructor () {
    super()
    this.state = { wells: [] }
  }

  componentDidMount() {
    this.props.fetchWells()
      .then(res => this.setState({ wells: res.wells }) )
  }

  render () {
    const { wells } = this.state
    return wells.length ? this.renderWells() : (
      <span>Loading wells...</span>
    )
  }
}

UPDATE: following the answer changing it to works!

  @native
  @JSImport("react-native-fbsdk-next", "AccessToken")
  object FBAccessToken extends js.Object {
    def getCurrentAccessToken(): Promise[FBAccessToken | Null] = native
  }
1 Answers

From what I see in the documentation here, getCurrentAccessToken is a function returning a Promise, so it needs to be declared with ():

def getCurrentAccessToken(): Promise[FBAccessToken | Null] = native

It's also worth double-checking the import path, since that documentation suggests that it's inside a top-level object called AccessToken, and not as the default export.

Related