copper-leaf / ballast

Opinionated Application State Management framework for Kotlin Multiplatform
https://copper-leaf.github.io/ballast/
BSD 3-Clause "New" or "Revised" License
147 stars 10 forks source link

Textfield adding extra characters on typing #40

Closed charlee-dev closed 1 year ago

charlee-dev commented 1 year ago

Hi. I've got an issue with my text field adding extra characters. I checked it using mutableStateOf and it works as expected. Code related to the email below, nothing fancy...

in Contract

internal object LoginContract {
    data class State(
        val email: String = "",
    )

    sealed interface Inputs {
        data class ChangeEmail(val newEmail: String) : Inputs
    }

    ...
}

in Content

    OutlinedTextField(
        text = state.email,
        onTextChange = { vm.trySend(LoginContract.Inputs.ChangeEmail(it)) },

    ...
}

in Input

internal class LoginInputHandler : KoinComponent, InputHandler<LoginContract.Inputs, LoginContract.Events, LoginContract.State> {
    override suspend fun LoginInput.handleInput(input: LoginContract.Inputs) = when (input) {
        is LoginContract.Inputs.ChangeEmail -> updateState { it.copy(email = input.newEmail) }

        ...
    }
}

https://user-images.githubusercontent.com/20965327/218436652-e41fa81b-0ca5-439c-9400-a5802fb48cdd.mp4

cjbrooks12 commented 1 year ago

Answered in the Slack channel, copying the solution here for anyone else who may have this issue:

I’ve seen similar issues to the problem, more generally, of managing TextField state in a StateFlow vs mutableStateOf. The typical solution is to use Dispatchers.Main.Immediate as the Dispatcher. Ballast currently uses Dispatchers.Default as the default for everything

Try adding this into the viewModel configuration, and see if it helps:

.dispatchers(
    inputsDispatcher = Dispatchers.Main.immediate,
    eventsDispatcher = Dispatchers.Main.immediate,
    sideJobsDispatcher = Dispatchers.IO,
    interceptorDispatcher = Dispatchers.Default
)

The thing to watch out for now is to make sure you’re not making API requests directly in the InputHandler without withContext(Dispatchers.IO)