shadaj / slinky

Write Scala.js React apps just like you would in ES6
https://slinky.dev
MIT License
656 stars 57 forks source link

React 16.6 feature support #196

Open shadaj opened 6 years ago

shadaj commented 6 years ago
ramnivas commented 6 years ago

static contextType will be awesome! Currently, we have several wrapper components whose sole purpose is to grab a context value and pass it as a prop to the underlying component (so that the underlying component can use it in componentDidMount and such lifecycle methods).

shadaj commented 5 years ago

The remaining two features are generally pretty incompatible with Scala (typing) / Scala.js (dynamic imports), so going to close this issue for now.

evbo commented 2 years ago

@shadaj I'm wondering does it make sense to reopen this for enabling React.lazy now that Scala.js has supported code-splitting for a while?

My slinky app is starting to get slooow, so I was hoping to use React.lazy presuming scalajs-bundler can handle the extra bundles it generates! Would you still consider this improvement?

evbo commented 2 years ago

@shadaj any words of wisdom how I could go about contributing a react Lazy support? Where in the code base might I refer to for how similar functionality is implemented? I'm not sure what your schedule is, but I don't think there's any other way to progressively load a slinky app other than enabling this feature so I might give it a try since I'm in desperate need!

Thanks for reopening :)

evbo commented 2 years ago

@shadaj where I'm a bit stuck implementing a facade for lazy is that it requires the return of a module with default export, not simply a ReactElement. Using Scala.JS dynamicImport, do you have a hunch or any tips on how to signify that type? A module is just a file, so does it mean literally the bundled file path (a string?)?

evbo commented 2 years ago

Okay, I think I have a working implementation of lazy that at least compiles. But I can't run it since scalajs-bundler doesn't yet support multiple outputs! @shadaj have you found a way around this?

Here's what I got so far:

trait LazyResult[P] extends js.Object {
  val default: FunctionalComponent[P]
}

@js.native
@JSImport("react", JSImport.Namespace, "React")
object ReactShim extends js.Object {
  final def `lazy`[P](f: js.Function0[js.Promise[LazyResult[P]]]): FunctionalComponent[P] = js.native
}

The only awkward bit is the props require you to explicitly instantiate the case class, but other than that it's not too ugly:

@react
object App {

  type Props = Unit

  val other = ReactShim.`lazy`(() => js.dynamicImport(new LazyResult[Other.Props]{
    override val default = Other.component
  }))

  val component = FunctionalComponent[Props] { props =>

    val (show, setShow) = useState[Boolean](false)

    div(
      "Hey!",
      button(onClick := (() => setShow(true))),
      if (show) {
        other(Other.Props("hello"))
      } else {
        Nil
      }
    )
  }
}