Firstly, it's recommended you read TYPES.md.
- Using JS components in Scala
- Using JS functional components in Scala
- Using JS ref-forwarding components in Scala
- Using Scala components from JS
- Using Scala components from JSX
- Determine your types.
- Props - Create a standard Scala.JS facade for the component's props object if it exists. Use
Null
otherwise. - State - Create a standard Scala.JS facade for the component's state object if it exists. Use
Null
otherwise. - Children - Determine whether the component uses
.props.children
and choose eitherChildren.None
orChildren.Varargs
accordingly. - Mounted Facade - Create a standard Scala.JS facade for the component's mounted instance if it contains additional API.
- Get a reference to the target component. Either:
@JSGlobal("Blah") // If you're not using modules
@JSImport("./blah.js", "Blah") // If you're using modules
@js.native
object BlahJs extends js.Object
or
val BlahJs = js.Dynamic.global.Blah // If you're not using modules
- Create the component by calling
JsComponent[Props, Children, State](raw)
whereraw
is the raw JS value of the component you created in the previous step. - (Optional) To attach a mounted facade (
F
), append.addFacade[F]
to yourJsComponent
.
Example:
import japgolly.scalajs.react._
import scalajs.js
/**
* Component-wrapper for collapse animation with react-motion for
* elements with variable (and dynamic) height.
*
* https://github.com/nkbt/react-collapse
*/
object ReactCollapse {
@JSGlobal("ReactCollapse")
@js.native
object RawComponent extends js.Object
@js.native
trait Measures extends js.Object {
val height: Double = js.native
val width: Double = js.native
}
type OnMeasure = js.Function1[Measures, Unit]
type OnRest = js.Function0[Unit]
@js.native
trait Props extends js.Object {
var isOpened: Boolean = js.native
var onMeasure: OnMeasure = js.native
var onRest: OnRest = js.native
}
def props(isOpened: Boolean,
onMeasure: Measures => Callback = _ => Callback.empty,
onRest: Callback = Callback.empty): Props = {
val p = (new js.Object).asInstanceOf[Props]
p.isOpened = isOpened
p.onMeasure = (measures: Measures) => onMeasure(measures).runNow()
p.onRest = onRest.toJsCallback // or alternatively: () => onRest.runNow()
p
}
val component = JsComponent[Props, Children.Varargs, Null](RawComponent)
}
Example for .addFacade[F]
.
// The JavaScript
class AddFacadeExample extends React.Component {
constructor(props) {
super(props);
this.state = { num1: 123, num2: 500 };
}
// method that needs to be accessible on the component
inc() {
this.setState({ num1: this.state.num1 + 1 });
}
render() {
return React.createElement("div", null, "State = ", this.state.num1, " + ", this.state.num2, this.props.children);
}
}
// The Scala facade
object AddFacadeExample {
@JSGlobal("AddFacadeExample")
@js.native
object RawComponent extends js.Object
@js.native
trait JsState extends js.Object {
val num1: Int
val num2: Int
}
@js.native
trait JsMethods extends js.Object {
def inc(): Unit = js.native
}
val component = JsComponent[Null, Children.None, JsState](RawComponent).addFacade[JsMethods]
}
Pretty much the same as above, just without state and a mounted-facade.
- Determine your types.
- Props - Create a standard Scala.JS facade for the component's props object if it exists. Use
Null
otherwise. - Children - Determine whether the component uses
.props.children
and choose eitherChildren.None
orChildren.Varargs
accordingly.
- Create the component by calling
JsFnComponent[Props, Children](x)
where x is:
- a
String
which is the name of the component. - an instance of
js.Dynamic
.
Pretty much the same as JsComponent
, just that you also specify the ref type
(i.e. the value of the reference value when the component sets it).
- Determine your types.
- Props - Create a standard Scala.JS facade for the component's props object if it exists. Use
Null
otherwise. - Children - Determine whether the component uses
.props.children
and choose eitherChildren.None
orChildren.Varargs
accordingly. - Ref target - Determine the type of value that a forwarded reference would provide. Usually this will be some HTML DOM.
- Create the component by calling
JsForwardRefComponent[Props, Children, Ref](x)
where x is:
- a
String
which is the name of the component. - an instance of
js.Dynamic
.
import japgolly.scalajs.react._
import org.scalajs.dom.html
import scala.scalajs.js
import scala.scalajs.js.annotation._
object FancyButton {
@JSGlobal("FancyButton")
@js.native
private object RawComp extends js.Object
val Component = JsForwardRefComponent[Null, Children.Varargs, html.Button](RawComp)
}
- Create a JS object representation of your component's props.
- Export a fn to JS that goes from your JS props object to JS
React.Element
. - Call the exported method from JS and use the result directly in your JS vdom.
import japgolly.scalajs.react._
import japgolly.scalajs.react.vdom.html_<^._
import scala.scalajs.js
import scala.scalajs.js.annotation._
object Example {
// Say this is the Scala component you want to share
val myScalaComponent = ScalaComponent.builder[String]("")
.render_P(name => <.div("My name is: ", name))
.build
// This will be the props object used from JS-land
trait JsProps extends js.Object {
val name: String
}
@JSExportTopLevel("MyScalaComponent")
def render(props: JsProps): raw.React.Element =
myScalaComponent(props.name).rawElement
}
import { MyScalaComponent } from 'your-scalajs-output';
class MyJsComponent extends React.Component {
render() {
return MyScalaComponent({ name: "Bob Loblaw" });
}
}
JSX is a little different than JS as it gets transpiled from code like
<Blah msg="abc" />
to React.createElement(Blah, { msg: "abc" })
.
To make that work nicely we actually want to create a new JS component and expose it.
- Create a JS object representation of your component's props.
- Contramap the component's props to your JS object representation and call
.toJsComponent
to create a real JS component. - Export the JS value of your new component (by calling
.raw
) - Use the exported component directly within JSX.
import japgolly.scalajs.react._
import japgolly.scalajs.react.vdom.html_<^._
import scala.scalajs.js
import scala.scalajs.js.annotation._
object Example {
// Say this is the Scala component you want to share
val myScalaComponent = ScalaComponent.builder[String]("")
.render_P(name => <.div("My name is: ", name))
.build
// This will be the props object used from JS-land
trait JsProps extends js.Object {
val name: String
}
@JSExportTopLevel("MyScalaComponent")
val myJsComponent =
myScalaComponent
.cmapCtorProps[JsProps](_.name) // Change props from JS to Scala
.toJsComponent // Create a new, real JS component
.raw // Leave the nice Scala wrappers behind and obtain the underlying JS value
}
import { MyScalaComponent } from 'your-scalajs-output';
class MyJsComponent extends React.Component {
render() {
return (
<MyScalaComponent name="Bob Loblaw" />
);
}
}