Code Monkey home page Code Monkey logo

scalajs-react's Introduction

scalajs-react

Lifts Facebook's React library into Scala.js and endeavours to make it as type-safe and Scala-compatible as possible.

Contents

Setup

SBT

// Minimal usage
libraryDependencies += "com.github.japgolly.scalajs-react" %%% "core" % "0.4.0"

// Test support including ReactTestUtils
libraryDependencies += "com.github.japgolly.scalajs-react" %%% "test" % "0.4.0" % "test"

// Scalaz support
libraryDependencies += "com.github.japgolly.scalajs-react" %%% "ext-scalaz70" % "0.4.0" // or
libraryDependencies += "com.github.japgolly.scalajs-react" %%% "ext-scalaz71" % "0.4.0"

Code:

// Typical usage
import japgolly.scalajs.react._ // React
import vdom.ReactVDom._         // Scalatags โ†’ React virtual DOM
import vdom.ReactVDom.all._     // Scalatags html & css (div, h1, textarea, etc.)

// Scalaz support
import japgolly.scalajs.react.ScalazReact._

Examples

Examples are included with this project. If you know Scala and React then that should have you up and running in no time.

If you'd like to see side-by-side comparisons of sample code taken from http://facebook.github.io/react/, do this:

  1. Checkout or download this repository.
  2. sbt fastOptJS
  3. Open example/side_by_side.html locally.

Differences from React proper

  • Rather than using JSX or React.DOM.xxx to build a virtual DOM, use ReactVDom which is backed by lihaoyi's excellent Scalatags library. (See examples.)
  • In addition to props and state, if you look at the React samples you'll see that most components need additional functions and in the case of sample #2, state outside of the designated state object (!). In this Scala version, all of that is heaped into an abstract type called Backend which you can supply or omit as necessary.
  • If you want to pass some plain text into a React component (as one of its children), then you need to wrap it in raw(). (It's a Scalatags thing.)
  • To keep a collection together when generating the dom, call .toJsArray. The only difference I'm aware of is that if the collection is maintained, React will issue warnings if you haven't supplied key attributes. Example:
    table(tbody(
      tr(th("Name"), th("Description"), th("Etcetera")),
      myListOfItems.sortBy(_.name).map(renderItem).toJsArray
    ))
  • To specify a key when creating a React component, instead of merging it into the props, call .withKey() before providing the props and children.
    val Example = ReactComponentB[String]("Eg").render(i => h1(i)).create
    Example.withKey("key1")("The Prop")

MOAR FP / Scalaz

Included is a Scalaz module that facilitates a more functional and pure approach to React integration. This is achieved primarily via state and IO monads. Joyously, this approach makes obsolete the need for a "backend".

State modifications and setState callbacks are created via ReactS, which is conceptually WriterT[M, List[Callback], StateT[M, S, A]] but caters to Scala's hopeless inability to infer types. They are applied via runState or runStateS for vanilla StateT monads (ie. without callbacks). Callbacks take the form of IO[Unit] and are hooked into HTML via ~~>, e.g. button(onclick ~~> T.runState(blah), "Click Me!").

Also included are runStateF methods which use a ChangeFilter typeclass to compare before and after states at the end of a state monad application, and optionally opt-out of a call to setState on a component.

See ScalazExamples for a taste. Take a look at the ScalazReact module for the source.

Testing

React.addons.TestUtils is wrapped in Scala and available as ReactTestUtils in the test module (see Setup). Usage is unchanged from JS.

To make event simulation easier, certain event types have dedicated, strongly-typed case classes to wrap event data. For example, JS like

React.addons.TestUtils.Simulate.change(t, {target: {value: "Hi"}})

becomes

ReactTestUtils.Simulate.change(t, ChangeEventData(value = "Hi"))

// Or alternatively,
ChangeEventData("Hi") simulate t

Also included is DebugJs, a dumping ground for functionality useful when testing JS. inspectObject is tremendously useful.

SBT

In order to test React and use ReactTestUtils you will need to make a few changes to SBT.

  • Add
jsDependencies += "org.webjars" % "react" % "0.11.1" % "test" / "react-with-addons.js" commonJSName "React"
  • Add: requiresDOM := true
  • Install PhantomJS.
  • Use jsEnv in Test to override ScalaJS's PhantomJS bind polyfill; copy from this project. (This is being tracked in ScalaJS #898)

Extensions

Scalatags

  • attr ==> (SyntheticEvent[_] => _) - Wires up an event handler.
    def handleSubmit(e: SyntheticEvent[HTMLInputElement]) = ...
    val html = form(onsubmit ==> handleSubmit)(...)
  • attr --> (=> Unit) - Specify a function as an attribute value.
    def reset() = T.setState("")
    val html = div(onclick --> reset())("Click to Reset")
  • boolean && (attr := value) - Make a condition optional.
    def hasFocus: Boolean = ...
    val html = div(hasFocus && (cls := "focus"))(...)

React

  • Where this.setState(State) is applicable, you can also run modState(State => State).
  • SyntheticEvents have aliases that don't require you to provide the dom type. So instead of SyntheticKeyboardEvent[xxx] type alias ReactKeyboardEvent can be used.
  • Because refs are not guaranteed to exist, the return type is wrapped in js.UndefOr[_]. A helper method tryFocus() has been added to focus the ref if one is returned.
    val myRef = Ref[HTMLInputElement]("refKey")

    class Backend(T: BackendScope[_, _]) {
      def clearAndFocusInput() = T.setState("", () => myRef(t).tryFocus())
    }
  • The component builder has a propsDefault method which takes some default properties and exposes constructor methods that 1) don't require any property specification, and 2) take an Optional[Props].
  • The component builder has a propsAlways method which provides all component instances with given properties, doesn't allow property specification in the constructor.
  • React has a classSet addon for specifying multiple optional class attributes. The same mechanism is applicable with this library is as follows:
    div(classSet(
      "message"           -> true,
      "message-active"    -> true,
      "message-important" -> props.isImportant,
      "message-read"      -> props.isRead
    ))(props.message)

    // Or for convenience, put all constants in the first arg:
    div(classSet("message message-active"
      ,"message-important" -> props.isImportant
      ,"message-read"      -> props.isRead
    ))(props.message)
  • Sometimes you want to allow a function to both get and affect a portion of a component's state. Anywhere that you can call .setState() you can also call focusState() to return an object that has the same .setState(), .modState() methods but only operates on a subset of the total state.
    def incrementCounter(s: ComponentStateFocus[Int]) = s.modState(_ + 1)

    // Then later in a render() method
    val f = T.focusState(_.counter)((a,b) => a.copy(counter = b))
    button(onclick --> incrementCounter(f))("+")

Gotchas

  • table(tr(...)) will appear to work fine at first then crash later. React needs table(tbody(tr(...))).
  • React doesn't apply invocations of this.setState until the end of render or the current callback. Calling .state after .setState will return the original value, ie. val s1 = x.state; x.setState(s2); x.state == s1 // not s2. If you want to compose state modifications (and you're using Scalaz), take a look at the ScalazReact module, specifically ReactS and runState.

Alternatives

Major differences:

  • Object-oriented approach.
  • Uses XML-literals instead of Scalatags. Resembles JSX very closely.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.