So we're in a react app. We have an input field, select field, or something else. So how do we interface with these. With the excellent closure support of Kotlin of course.

Type Aliases

I recommend using type aliases. To define common event types. Making the code base cleaner, and refactoring easier.

package animus.design.web

import org.w3c.dom.events.Event

typealias EventHandlerFunction = (Event) -> Unit

This is your most standard event handlard function. Your given a dom unit as a function input. Then expecting nothing back.

Unsafe Casting

safe

So as I've said. Java script is a no mans land of barbed wire and land mines. Ready to blow your legs into pulpy smithereens. We try and be like Linus with our safety blanket of a type system. But sometimes we need to venture out into that no mans land. There is the typical kotlin casting like.

{ event ->
    var target = event.target as HTMLInputElement
}

That doesn't work. It was ever so much fun to see a JVM NPE in the chrome console window. Instead we can call it as a JS object, and then do an unsafe cast, like:

{ event: Event ->
        val target = event.target?.asJsObject().unsafeCast<HTMLSelectElement>()

}

Wait Why

Hello rabbit hole my old friend. So Event has an interface definition that follows like this.

open val target: EventTarget?

Deeper, you got it:

public external abstract class EventTarget {
    fun addEventListener(type: String, callback: EventListener?, options: dynamic = definedExternally): Unit
    fun addEventListener(type: String, callback: ((Event) -> Unit)?, options: dynamic = definedExternally): Unit
    fun removeEventListener(type: String, callback: EventListener?, options: dynamic = definedExternally): Unit
    fun removeEventListener(type: String, callback: ((Event) -> Unit)?, options: dynamic = definedExternally): Unit
    fun dispatchEvent(event: Event): Boolean
}

Great, when I want an event handler for the text field. I usually need the value, that's why we need to say the target is derived of an HTMLElement

Back to our Regular Programming

Basically when we take in these event handlers, for on click, on change, etc. We need to specify the html element.

HTML Select Element

{ event: Event ->
        val target = event.target?.asJsObject().unsafeCast<HTMLSelectElement>()
}

HTML Input Element

{ event: Event ->
        val target = event.target?.asJsObject().unsafeCast<HTMLInputElement>()
}

Etc.

So basically with this you can extrapolate that with Intellij. Just start typing HTML, and it will list the elements you can cast too.

However this isn't useful for just HTML elements. Let's say that we have a drop down select field. It contains a range of numbers. In javascript the value is represented inside the dom generally as a string. That's the safest bet. However in your interfaces, and class definitions. You want it to be a Number or Int. Then you can cast it over. Now stop, you're taking a big risk here. This can throw an exception. However if you're the single source of truth, you can cast without regard.

val selectedValue = state.selectedValue?.unsafeCast<Int>()

So in the above I'm assuming the selected value is stored in a react component state. This allows nulls. But what if we don't allow nulls, we instead assume 0 is an empty value. Elvis to the rescue!

val selectedValue = state.selectedValue?.unsafeCast<Int>() ?: 0