KVision
Primary version
Primary version
  • KVision Guide
  • Introduction
  • Migration
    • Migration from 8.x to 9.x
    • Migration from 7.x to 8.x
    • Migration from 6.x to 7.x
    • Migration from 5.x to 6.x
    • Migration from 4.x to 5.x
  • 1. Getting Started
    • Setting Up
    • Modules
    • Creating a New Application
    • Development Workflow
    • Hot Module Replacement
    • Debugging
    • Building For Production
  • 2. Frontend Development Guide
    • UI Structure
    • Root Container
    • Theming
    • Dark mode
    • Type safe CSS Properties
    • Basic Components
    • Icons and Images
    • Buttons and Toolbars
    • Layout Containers
    • Events
    • Working with State
    • DOM Bindings & Lifecycle Hooks
    • W3C, Snabdom, and KVision Elements
    • Forms
    • Form controls
    • Drag and drop
    • Internationalization
    • Adding custom tags (SVG example)
    • Custom components
  • 3. Optional UI Functionality (via modules)
    • Using Redux
    • Bootstrap
      • Navigation and menus
      • Tooltips and popovers
      • Modals, windows and toasts
    • Charts
    • Toasts
    • Tabulator Tables
    • Handlebars.js Templates
    • JS Routing with Navigo
    • jQuery Bindings
    • Using REST Services
  • 4. Integrating With Javascript Libraries
    • Integrating With React Components
  • 5. Fullstack Development Guide
    • Select Remote
    • Tom Select Remote
    • Tom Typeahead Remote
    • Tabulator Remote
  • FAQ
  • Useful References
Powered by GitBook
On this page
  • Dependencies
  • Simple components
  • Advanced components
  • Accessing internal API of React components
  • Using KVision components as React children
  • Resources
  1. 4. Integrating With Javascript Libraries

Integrating With React Components

Previous4. Integrating With Javascript LibrariesNext5. Fullstack Development Guide

Last updated 17 days ago

is one of the most popular JavaScript libraries for building user interfaces. It's architecture is based on encapsulated components that manage their own state. There are a lot of free React components available, you can find a list of most popular on .

Although KVision offers a rich set of different components, there will be occasions when you will need to use something that KVision does not offer. Fortunately both KVision and Kotlin/JS ecosystem allows you to include any dependencies in your project. And KVision has full support for embedding external React components inside your application. React components are standardized, so it's fairly easy to learn how to use them.

Note: KVision support for React components is based on from JetBrains, so it's good to know how to use these libraries. You can learn the most important things from .

Dependencies

To use React component just add kvision-react module and the required NPM dependencies to your build.gradle.kts file.

kotlin {
// ...
    sourceSets["jsMain"].dependencies {
    // ...
        implementation(npm("react-awesome-button", "6.5.1"))
        implementation(npm("prop-types", "*")) // required by react-awesome-button

        implementation(npm("react-ace", "14.0.1"))
        
        implementation("io.kvision:kvision:$kvisionVersion")
        implementation("io.kvision:kvision-react:$kvisionVersion")
        // ... other KVision modules
    }
}

Simple components

import react.ComponentType
import react.PropsWithChildren

external interface ReactButtonProps : PropsWithChildren {
    var type: String
    var size: String
    var action: (dynamic, () -> Unit) -> Unit
}

@JsModule("react-awesome-button")
external val reactButtonModule: dynamic

val ReactButton: ComponentType<ReactButtonProps> = reactButtonModule.AwesomeButtonProgress

Having this declaration, you can use the component with the react { ... } DSL builder function.

react {
    ReactButton {
        type = "primary"
        size = "large"
        action = { _, next ->
            window.setTimeout({
                next()
            }, 3000)
        }
        +"React progress button"
    }
}

Advanced components

React components can be stateful and can maintain internal state data. With KVision it's possible to logically relocate this internal state from React component into KVision component, where it can be accessed from the other parts of the application.

import react.ComponentType
import react.PropsWithChildren

external interface ReactAceProps : PropsWithRef<dynamic>, PropsWithChildren {
    var value: String
    var mode: String
    var theme: String
    var onChange: (String) -> Unit
}

@JsModule("react-ace")
external object reactAce {
    val default: dynamic
}

@JsModule("ace-builds/src-noconflict/ace.js")
external val ace: dynamic

@JsModule("ace-builds/src-noconflict/mode-kotlin.js")
external val modeKotlin: dynamic

@JsModule("ace-builds/src-noconflict/theme-monokai.js")
external val themeMonokai: dynamic

val AceEditor: ComponentType<ReactAceProps> = reactAce.default.default

class App : Application() {
    init {
        useModule(ace)
        useModule(modeKotlin)
        useModule(themeMonokai)
    }
    // ...
}

Now you can use the component with advanced form of the DSL builder functionreact(initialState) { getState, changeState -> ... }.


val ace = react("some initial code") { getState, changeState ->
    AceEditor {
        value = getState()
        mode = "kotlin"
        theme = "monokai"
        onChange = { value -> changeState { value } }
    }
}
button("Get the code").onClick {
    console.log(ace.state)
}
button("Set the code").onClick {
    ace.state = "some new code"
}

You initialize the KVision React component with some initial state, which can be any type T you need. You use getState(): () -> T function to retrieve the current state and assign the correct value to the React component input property. And you can use changeState(): ((T) -> T) -> Unit function to modify the state (most of the time this function will be used with some React callbacks). After creating this two-way bindings you can both read and change the current state of the component with its .state property.

Accessing internal API of React components

Sometimes it may be necessary to access the internal api of a React component, beyond the attributes exposed in the interface. To do this, you can use the ref variable provided by PropsWithRef<T>, which is part of the kotlin-react library:

    var internalEditor: dynamic = null

    val ace: React<String>

    init {
        ace = react(state) { getState, changeState ->
            val onContainerCallback = useRefCallback<dynamic> { comp ->
                internalEditor = comp?.editor
            }
            AceEditor {
                value = getState()
                // other parameters
                ref = onContainerCallback
            }
        }
    }
    
    fun moveCursorTo(line: Int, col: Int) {
        internalEditor?.moveCursorTo(line, col)
    }

Using KVision components as React children

Most React components can have children. Typically you can easily add other React components as React children. But you may also use KVision components with a help of kv helper function.

    import io.kvision.react.kv
    
    root("kvapp") {
        react {
            ElTabs {
                ElTabsPane {
                    label = "Tab"
                    kv {
                        textInput(value = "KVision text input inside React")
                    }
                }
            }
        }
    }

Resources

When using React components you will also need to include resources (like CSS). Use useModule function inside some init {} block for this purpose.

@JsModule("react-awesome-button/dist/themes/theme-blue.css")
external val cssThemeBlue: dynamic

init {
    useModule(cssThemeBlue)
}

Let's start with a simple example and component. First you need to declare the type of data being passed into the component.

Let's use an advanced ACE code editor with component. The basic declaration is similar to the previous example (of course the component has a lot more properties then covered by this example).

React
this page
NPM
Kotlin Wrappers
this tutorial
react-awesome-button
react-ace