All Projects → Ahnfelt → React4s

Ahnfelt / React4s

Licence: mit
Production ready React wrapper for Scala.js - composable lifecycle - no memoization, no macros, no implicits.

Programming Languages

scala
5932 projects
scalajs
39 projects

Projects that are alternatives of or similar to React4s

Re Jok
A React UI Component library built with styled-components
Stars: ✭ 102 (-14.29%)
Mutual labels:  components
React Forms
React library for rendering forms.
Stars: ✭ 111 (-6.72%)
Mutual labels:  components
Vcomponents
VComponents is a SwiftUI framework that contains 40+ customizable UI components
Stars: ✭ 117 (-1.68%)
Mutual labels:  components
Gu
A web ui library for Go. [DEPRECATED]
Stars: ✭ 102 (-14.29%)
Mutual labels:  components
React Collection Helpers
A suite of composable utility components to manipulate collections.
Stars: ✭ 109 (-8.4%)
Mutual labels:  components
Redux Autoform
Create Redux-Forms dynamically out of metadata
Stars: ✭ 113 (-5.04%)
Mutual labels:  components
Uniforms
A React library for building forms from any schema.
Stars: ✭ 1,368 (+1049.58%)
Mutual labels:  components
Ant Design Plus
基于ant-design封装的偏业务组件
Stars: ✭ 118 (-0.84%)
Mutual labels:  components
React Multi Select
A Multi Select component built with and for React
Stars: ✭ 111 (-6.72%)
Mutual labels:  components
Reactstrap
Simple React Bootstrap 5 components
Stars: ✭ 10,207 (+8477.31%)
Mutual labels:  components
Server Components Mdx Demo
React server components + MDX
Stars: ✭ 102 (-14.29%)
Mutual labels:  components
Atlas.js
A component-based Node.js library to reduce boilerplate and provide sane project structure 🍻
Stars: ✭ 108 (-9.24%)
Mutual labels:  components
Composite
A component-based OS
Stars: ✭ 113 (-5.04%)
Mutual labels:  components
Pd Select
vue components ,like ios 3D picker style,vue 3d 选择器组件,3D滚轮
Stars: ✭ 101 (-15.13%)
Mutual labels:  components
Viewcontroller
📌 A view controller manages a set of views that make up a portion of your app’s user interface,it aims to make ui develop change more clear and flexible.(ViewControler 是一种界面开发组件化实现方式,利用它可以将一些复杂的 UI 界面开发组件化.)
Stars: ✭ 117 (-1.68%)
Mutual labels:  components
Data Components
♻️ Tiny component structure for web applications
Stars: ✭ 100 (-15.97%)
Mutual labels:  components
Candela
Visualization components for the web
Stars: ✭ 112 (-5.88%)
Mutual labels:  components
Vue Cion Design System
CION - Design system boilerplate for Vue.js
Stars: ✭ 119 (+0%)
Mutual labels:  components
Swiftui Kit
A SwiftUI system components and interactions demo app
Stars: ✭ 1,733 (+1356.3%)
Mutual labels:  components
React Workshop
⚒ 🚧 This is a workshop for learning how to build React Applications
Stars: ✭ 114 (-4.2%)
Mutual labels:  components

React4s

React4s is a Scala library for frontend UI. It wraps Facebook's React library. It exposes an API that makes it easy to write plain and simple Scala code for your components. You get the indispensable shouldComponentUpdate() for free, no callback memoization required. It uses no macros, no implicits and no complicated types.

See the documentation at react4s.orgView TodoMVC

resolvers += Resolver.sonatypeRepo("snapshots")
libraryDependencies += "com.github.ahnfelt" %%% "react4s" % "0.10.0-SNAPSHOT"

Writing a component

case class OkCancel(label : P[String]) extends Component[Boolean] {
    override def render(get : Get) = E.div(
        E.div(Text(get(label)),
        E.div(
            E.button(
                Text("OK"),
                A.onClick(_ => emit(true))
            ),
            E.button(
                Text("Cancel"),
                A.onClick(_ => emit(false))
            )
        )
    )
}

This defines a component OkCancel that takes one String "prop" named label. You read props, state, etc. with the get object, which is only available where you can safely read from these. The Boolean in Component[Boolean] says that this component emits Boolean messages, which is done with the emit(...) method. The render() method is what renders your component, and the component is rerendered automatically when the props or state change. The E, A and S objects provide methods for building the Virtual DOM.

Emitting messages instead of taking in callbacks via props is a departure from the usual React API, and is how you get shouldComponentUpdate() for free. It also clearly separates input (props) from output (callbacks).

You can use a component like this: Component(OkCancel, "Would you like some icecream?"). The first argument is the components companion object. The remaining arguments are the props for the component.

Keeping state

case class Counter() extends Component[NoEmit] {
    
    val okClicks = State(0)
    val cancelClicks = State(0)
    
    def onClick(ok : Boolean) = {
        if(ok) {
            okClicks.modify(_ + 1)
        } else {
            cancelClicks.modify(_ + 1)
        }
    }
    
    override def render(get : Get) = E.div(
        Component(OkCancel, "Would you like some icecream?").withHandler(onClick),
        E.hr(),
        E.div(Text("You've clicked OK " + get(okClicks) + " times.")),
        E.div(Text("You've clicked Cancel " + get(cancelClicks) + " times."))
    )
    
}

The State type allows the library to detect when you update the state, so it can rerender your component. You can read it with eg. okClicks() and update it with eg. okClicks.set(42) or okClicks.modify(_ + 1).

Styles and CSS

case class OkCancel(label : P[String]) extends Component[Boolean] {
    override def render(get : Get) = E.div(
        E.div(Text(get(label)), S.color.rgb(0, 0, 255)),
        E.div(
            E.button(
                FancyButtonCss,
                Text("OK"),
                A.onClick(_ => emit(true))
            ),
            E.button(
                FancyButtonCss,
                Text("Cancel"),
                A.onClick(_ => emit(false))
            )
        )
    )
}

The above uses one inline style S.color.rgb(0, 0, 255) and one css class FancyButtonCss. The css class is defined as follows:

object FancyButtonCss extends CssClass(
    S.cursor.pointer(),
    S.border.px(2).solid().rgb(0, 0, 0),
    S.color.rgb(0, 0, 0),
    S.backgroundColor.rgb(255, 255, 255),
    Css.hover(
        S.color.rgb(255, 255, 255),
        S.backgroundColor.rgb(0, 0, 0)
    )
)

It styles a button to be white with a black border, and black with white text when the mouse is hovered over it. The resulting <style>...</style> will be added to the DOM the first time FancyButtonCss is used to render a component.

Binding it to the DOM

object Main extends js.JSApp {
    def main() : Unit = {
        val component = Component(Counter)
        ReactBridge.renderToDomById(component, "main")
    }
}

Just create the component and call renderToDomById. The "main" argument is the ID refering to an existing HTML element, eg. <div id="main"></div>.

Performance

In React, you implement shouldComponentUpdate() to avoid rerendering unrelated components when your model is updated. In React4s, this method is already implemented for you. It uses Scala's != operator to check if any props changed, and only updates the component if either the props changed or the state has been updated. That means that for everything that hasn't been reallocated, it just compares the references, and thus doesn't traverse deep into the props.

Beware that what you pass via props must be immutable and have structural equality. You can't pass mutable objects or functions as props, or you will get a stale view or a slow view respectively. However, it's completely safe to pass immutable collections and immutable case classes.

Lifecycle

image

This is the complete component lifecycle for React4s. It's simpler than plain React because the React4s model makes the assumption that your props are immutable and have structural equality.

  1. When your component is added to the Virtual DOM, the constructor is invoked.
  2. Before each render, the componentWillRender() method is called. Here you can update any state that depends on props that have changed.
  3. Then in render(), you'll return the Virual DOM that displays your component. State updates are not allowed during this call.
  4. When your component is removed from the Virtual DOM, componentWillUnmount() is called.

The component will only be rerendered when your props have changed, as defined by Scala's structural inequality !=, or your state has been updated. The state is considered updated when you've called update() explicitly or called .set(...) or .modify(...) on State objects with a value that's different from the previous according to the != operator.

You can attach Attachables that listen on these lifecycle events, and React4s comes with three of those: Timeout, Debounce and Loader. See how they're used in the Online Examples.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].