https://kotlinlang.org logo
a

Arjan van Wieringen

07/09/2022, 6:23 AM
I have the issue that I have an
Input
which on recomposition doesn't update the UI with the latest value, but it is visible in the DOM tree. The code is a bit like this
Copy code
@Composable fun EditableTitle(value: String, onInputChange: (String) -> Unit = {}) {
    var editMode by remember { mutableStateOf(false) }
    val nativeElement = remember { mutableStateOf<HTMLInputElement?>(null) }

    // I need to use this as a hack to overwrite the value so that it works
    LaunchedEffect(value) {
        nativeElement.value?.value = value
    }

    LaunchedEffect(editMode, nativeElement.value) {
        when {
            editMode -> nativeElement.value?.select()
            !editMode && nativeElement.value != null -> {
                nativeElement.value?.value?.also(onInputChange)
                nativeElement.value?.blur()
            }
        }
    }

    Input(InputType.Text) {
        placeholder("Enter list name...")
        defaultValue(value) // when I use value(value) it blocks my UI
        refState(nativeElement) // this one uses DisposableEffect to fetch the nativeElement

        if (editMode && evt.key == "Enter") {
            editMode = false
        }
    }
}
I do some stuff with some other state variables to make it more UI friendly. The
value
parameter can be updated externally as well and when it has been modified at least once in the above Composable, the UI doesn't update if the input value into the composable is changed. When I use the
LaunchedEffect(value)
it works. Is this logical?
o

Oleksandr Karpovich [JB]

07/11/2022, 11:02 AM
defaultValue
sets only the initial value of the input. Subsequent changes of
defaultValue
won't do anything. Ideally, no LaunchedEffect(value) should be needed and using
value
should not block the UI. Do you have a minimal reproducer when
value(value)
leads to UI freeze?
a

Arjan van Wieringen

07/11/2022, 3:17 PM
Since it is a very basic use case the most obvious thing will be that I am being dumb. So I will create a minimal example and hopefully prove otherwise
Okay, so I've reduced to this:
Copy code
fun main() {
    renderComposable("root") {
        MySimpleInput("Foo")
    }
}

@Composable fun MySimpleInput(
    value: String,
) {
    Input(InputType.Text) {
        value(value)
    }
}
I can't type anything in the input.
When I remove
value(value)
I can type.
And this works:
attr("value", value)
Looking in the source:
Copy code
fun defaultValue(value: Number): InputAttrsScope<ValueType> {
        attr("value", value.toString())
        return this
    }
I don't know what to say.....
h

hfhbd

07/11/2022, 4:34 PM
Do you want to update the value? Then you need a mutableState
a

Arjan van Wieringen

07/11/2022, 4:51 PM
Yes of course I want that, but I still would need to use
value
somehow right? So how would I do that then? Because even a minimal example freezes my input element.
Copy code
prop({ el: HTMLInputElement, v: String -> el.value = v}, value)
This also works
h

hfhbd

07/11/2022, 4:53 PM
This should work:
Copy code
fun main() {
    renderComposable("root") {
        var input by remember { mutableStateOf("Foo") }
        MySimpleInput(input) { input = it }
    }
}

@Composable fun MySimpleInput(
    value: String,
    onInput: (String) -> Unit
) {
    Input(InputType.Text) {
        value(value)
        onInput { onInput(it.value) }
    }
}
🙏 1
a

Arjan van Wieringen

07/11/2022, 4:59 PM
You're right. But it got me totally confused, because it is subtlety different than Angular / React. For me it doesn't make sense that input-changes are not even registered when I don't update the mutableInput again. You know what I mean?
h

hfhbd

07/11/2022, 5:02 PM
Yeah, you need to update the state manually. You could use the inputs uncontrolled, but this is not the (preferred) way. The android docs cover this topics deeply: https://developer.android.com/jetpack/compose/mental-model
a

Arjan van Wieringen

07/11/2022, 6:29 PM
Thanks a lot for helping again 🙂
7 Views