You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

82 lines
2.6 KiB

### How to use HTML based @Composable functions in JS?
Useful links:
- [Use Kotlin Code from JS](https://kotlinlang.org/docs/js-to-kotlin-interop.html)
- [JavaScript modules](https://kotlinlang.org/docs/js-modules.html)
- [webpack bundling](https://kotlinlang.org/docs/js-project-setup.html#webpack-bundling)
1) @Composable functions can't be invoked from JS directly (because every @Composable function has implicit parameters added by compiler plugin)
2) We can wrap @Composable functions into some usual function (with [@JsExport](https://kotlinlang.org/docs/js-to-kotlin-interop.html#jsexport-annotation)) which can be invoked in JS as is.
3) Every call to a "wrapping" function will create a composition (part of DOM controlled by Compose runtime)
4) It's important to `dispose` a composition when it's not needed.
5) The composition's state can be controlled by creating an arbitrary `Controller` class, which implements and exposes necessary functions for state updates.
### Simplified example (see full example in `src/jsMain`):
```kotlin
// Composables.kt
@JsExport
abstract class ComposeCounterAppController {
abstract fun setCount(newCount: Int)
abstract fun dispose()
}
@JsExport
fun ComposeCounterApp(rootId: String, onCountChange: (Int) -> Unit = {}): ComposeCounterAppController {
var count: Int by mutableStateOf(0)
val composition = renderComposable(rootElementId = rootId) {
// Counter is a @Composable function
// see full example in src/jsMain
Counter(count) {
count = it
onCountChange(count)
}
}
return object : ComposeCounterAppController() {
override fun setCount(newCount: Int) {
count = newCount
}
override fun dispose() {
composition.dispose()
}
}
}
```
Then in JS we can use `ComposeCounterApp` function:
```javascript
// see src/jsMain/resources/use_compose.js file for a full example
counterController = MyComposables.ComposeCounterApp('counterByCompose', (newCount) => {
console.log(`Counter was updated. New value = ${newCount}`);
});
```
The module name was overridden to make it convenient for usage in JS:
```
// webpack.config.d/configModuleName.js
config.output = config.output || {};
config.output.library = "MyComposables";
```
### Building and using the output
```
./gradlew jsBrowserProductionWebpack
```
This will produce the output in `build/distributions`.
Then we can use exported functions from `web-compose-in-js.js` (the filename is defined by the project/module name).
```html
<script src="web-compose-in-js.js"></script>
<script src="use_compose.js"></script>
```