lbp
lbp

Reputation: 113

In scalajs with scalajs-react how do I pass a scalajs defined component to a javascript defined component?

My issue is also here: https://gist.github.com/somanythings/8c3d34de754af311d7826ea837d160b4

In using scalajs with japgolly's scalajs-react (https://github.com/japgolly/scalajs-react) library. I am trying to wrap the griddle grid http://griddlegriddle.github.io/Griddle/customization.html I want a custom column, and to do that requires I pass a columnMetadata structure which includes a component.

When I do, I can render a scalajs defined component that doesn't have properties, but if I try to access properties through renderP, or scope through renderS, they are both undefined in the scope of the render function. If I debug in the browser, they names are bound, and do have the expected values.

When I break on

def renderP(f: (DuringCallbackU[P, S, B], P) => ReactElement): Out =
  render($ => f($, $.props))

then $.props is undefined

What am I missing? Is it a simple typing issue in the ReactComponentB dispatching. Is it somehow related to https://github.com/japgolly/scalajs-react/issues/157, and I just haven't seen how?

// Confusion over how to pass a scalajs defined component to a javascript defined component

object GriddleComponentWrapper {
  // for customComponent I've tried js.Any, ReactComponentU 
  @ScalaJSDefined
  class ColumnMeta(val columnName: String, val order: Int, val customComponent: ReactClass=null) extends js.Object
}

case class GriddleComponentWrapper(results: js.Any, //Seq[Map[String, Any]],
                                   columns: Seq[String],
                                   columnMeta: Option[Seq[ColumnMeta]] = None,
                                   showSettings: Boolean = true,
                                   showFilter: Boolean = true
                                  ) {
  def toJS = {
    val p = js.Dynamic.literal()
    p.updateDynamic("results")(results)
    p.updateDynamic("columns")(columns)
    p.updateDynamic("showSettings")(showSettings)
    p.updateDynamic("showFilter")(showFilter)

    (columnMeta).foreach { case cm => p.updateDynamic("columnMetadata")(cm.toJsArray) }

    p
  }

  def apply(children: ReactNode*) = {
    val f = React.asInstanceOf[js.Dynamic].createFactory(js.Dynamic.global.Bundle.griddle) // access real js component , make sure you wrap with createFactory (this is needed from 0.13 onwards)
    f(toJS, children.toJsArray).asInstanceOf[ReactComponentU_]
  }

}

object MyTestGrid {

  @js.native
  class ColumnMetaProps(val data: js.Object, val rowData: js.Object, val metadata: js.Object) extends js.Object

  // I've tried making the Props argument js.Dynamic, and also the ColumnMetaProps above
  @JSExport
  val testComp = ReactComponentB[js.Dynamic]("Mine").renderP(
    (sc, props: js.Dynamic) => {
      //when debugging this in the browser, 'sc' and 'props' have inspectable object values with the expected members in the browser 
      //dev tools, BUT, they're undefined 
      log.info(s"what is ${sc.props}")
      log.info(s"what is $props")
      val string: Frag = if (!js.isUndefined(props)) props.data.toString() else "nothing!"
      <.h1(string)
    }).build

   @JSExport 
   val aCompletelyStaticComponentWithNoPropsWillWork = ReactComponentB[js.Dynamic]("MyStaticComponent").renderP(
    (sc, props: js.Dynamic) =>  <.h1("this renders!!") ).build


// am I passing the right thing to columnmeta with testComp.reactClass? 
 val columnMeta = (new ColumnMeta("c1", 1, testComp.reactClass) :: Nil).toJsArray

  val results = Seq(
    js.Dynamic.literal("c1" -> "row1c1", "c2" -> "row1c2"),
    ).toJsArray

  val component = ReactComponentB[js.Dynamic]("MyTestGrid")
    .render_P {
      props =>
        GriddleComponentWrapper(results, columns = "c1" :: "c2" :: Nil, columnMeta = Some(columnMeta))()
    }.build

  def apply() = component
}

Upvotes: 2

Views: 481

Answers (1)

Golly
Golly

Reputation: 1330

React.JS requires that props and state always be an object (or null). Using a single Scala value like a primitive or case class instance, causes exceptions in React.JS. Therefore in scalajs-react, in order to allow users to use any props/state types in a type-safe manner, under the hood an object with a single key of "v" is used. I.e. instead of using 123 directly, {v:123} is used.

You likely will need to accommodate that boxing in your code.

Now, in the next major version (see the "neo" branch), the representations of components vastly improved such that there is no more hidden magic like I just described. Whereas v0.x wasn't designed to facilitate JS↔Scala component interop, it will be explicit, obvious, and hopefully trivial in neo.

Upvotes: 4

Related Questions