Skip to content

fluidsonic/fluid-react

Repository files navigation

fluid-react

Maven Central Kotlin React #fluid-libraries Slack Channel

Kotlin/JS wrapper for React, React Router and react-helmet-async.

  • Similar to kotlin-react.
  • Nicer and consistent API. Easier to use.
  • Not multiplatform. Optimized for Kotlin/JS instead.
  • Lower size and performance overhead.
  • More type safety, esp. around hooks.
  • With new Concurrent Mode in mind, thus depending on experimental React releases.
  • Props allow class instead of just external interface.
  • Updates of local properties delegated with by useState(…) are reflected immediately.
  • Support for coroutines with CoroutineScope(…) { … }, useCoroutineScope() and useFlow(…).
  • @DslMarker colors.
  • Experimental and IR compiler only. Relies on compiler-internal behavior until KT-10468 is solved.
  • Contributions welcome 😃
  • Kotlin/JS-optimized CSS library with nice API in the works.

Notable differences in behavior

  • Components created with react.component() are memoized by default unless they have children (react.componentWithChildren()).
  • Memoization of components created with react.component() or added by RComponent.memo() use equals() to compare Props. You must ensure that your props implement equals() in order to benefit from memoization.
  • Hook dependencies use equals() instead of ===. They don't need to be an Array nor is the same amount of dependencies needed for each render.
  • Router routes are exact, strict and sensitive by default.

Installation

build.gradle.kts:

dependencies {
	implementation("io.fluidsonic.react:fluid-react-dom:0.13.0") // basis module

	implementation("io.fluidsonic.react:fluid-react-coroutines:0.13.0") // optional coroutine support
	implementation("io.fluidsonic.react:fluid-react-helmet:0.13.0")     // optional dynamic metadata (react-helmet-async)
	implementation("io.fluidsonic.react:fluid-react-router-dom:0.13.0") // optional routing (react-router)
}

Example

import io.fluidsonic.react.*
import kotlinx.browser.*

fun main() {
	val body = checkNotNull(document.body)
	val container = document.createElement("div").also(body::appendChild)

	react.createRoot(container).render {
		+"Hello world"

		EmojiContainer(EmojiContainerProps("😍")) { strong { +"cool" } }
	}
}

val EmojiContainer by react.componentWithChildren { props: EmojiContainerProps, children ->
	var count by useState(3)

	useEffect(count) {
		val timerId = window.setTimeout({ count += 1 }, 2000)

		cleanup { window.clearTimeout(timerId) }
	}

	h1 { +"Your emoji, $count times 🎉" }
	button {
		attrs.onClick = { count += 1 }
		+"Add one"
	}
	ol {
		repeat(count) {
			li {
				+props.emoji
				+" "
				children()
			}
		}
	}
}

class EmojiContainerProps(val emoji: String)

Also check out the playground and run it from IntelliJ IDEA.