I've been trying my hand at full-stack Kotlin with React and ran into a sticky situation with useState
and coroutines. For those with more knowledge of React, this may seem obvious, but it took me a while to work through.
My initial component was attempting to update state from within a coroutine loop:
import react.*
import kotlinx.coroutines.*
import react.FC
import react.dom.html.ReactHTML.div
import react.dom.html.ReactHTML.h1
private val scope = MainScope()
val counterComponent = FC<Props> {
var counter by useState(0)
useEffectOnce {
scope.launch {
while (true) {
counter++
delay(1000)
}
}
}
div {
h1 { +"Count: $counter" }
}
}
The result is a component that displays '0' for a second and then continues to display '1' and never increases. After many println
, useCallback
, and Google searches I found this from the React FAQs. The behavior of the alert
is the same that I was seeing in my coroutine. The counter was being incremented, but it was always being incremented from '0' to '1'. After reading up on useRef
and poking around the React FAQ more, I found the solution.
import react.*
import kotlinx.coroutines.*
import react.FC
import react.dom.html.ReactHTML.div
import react.dom.html.ReactHTML.h1
private val scope = MainScope()
val counterComponent= FC<Props> {
val (_, forceUpdate) = useReducer<Int, Int>({ x, _ -> x + 1 }, 0)
val counter = useRef(0)
val update = useCallback {
counter.current = (counter.current ?: 0) + 1
forceUpdate(0)
}
useEffectOnce {
scope.launch {
while (true) {
update()
delay(1000)
}
}
}
div {
h1 { +"Count: ${counter.current}" }
}
}
Kotlin has some additional functions in useRefState
, useRefCallback
, and useRefValue
which may be helpful, but I haven't figured out how to use them yet.
Top comments (0)