Skip to content

[Question] Sealed class state, smart cast and container outdated state #191

@franka107

Description

@franka107

Hello, thank you for this amazing library!

I am managing my state using this sealed class:

sealed class ExampleState {
    object Loading : ExampleState()
    object Empty : ExampleState()
    data class Success(
        val firstName: String,
        val lastName: String,
        val age: Int
    ) : ExampleState()

    data class Error(val failure: Failure) : ExampleState()
}

And the declaration of my container:

//I could start my container with ExampleState.Empty,
// but I'll start with this state to help you understand the problem
override val container: Container<ExampleState, Nothing> =
        container(ExampleState.Success("Initial", "Initial", 0))

When trying to use this example intent:

private fun setExampleDataIntent() = intent {
    if (state is ExampleState.Success) {
        println(state.age) // <- IDE error on this line
    }
}

The IDE throws this error: "Smart cast to ExampleState.Success is impossible because state is a property that has an open or custom getter". However, with this code change, I managed to resolve the error:

private fun setExampleDataIntent() = intent {
    val uiState = state
    if (uiState is ExampleState.Success) {
        println(uiState.age) // 0
    }
}

But now I encountered a deeper problem. It turns out that with this implementation, the state I get from val uiState = state does not update after using the reduce() method. Take a look at the following example:

private fun setExampleDataIntent() = intent {
    val uiState = state
    if (uiState is ExampleState.Success) {
        println(uiState.age) // 0
        reduce { uiState.copy(
            firstName = "John",
            lastName = "Doe",
            age = 18
        ) }
        println(uiState.age) // 0 <- Error here, this should print 18
        println((state as ExampleState.Success).age) // 18
    }
}

This makes my solution unsafe, as the state may be outdated for the subsequent lines of code after the reduce operation. Another way to solve this problem is to cast every time I want to access the state:

private fun setExampleDataIntent() = intent {
    println((state as ExampleState.Success).age) // 0
    reduce {
        (state as ExampleState.Success).copy(
            firstName = "John",
            lastName = "Doe",
            age = 18
        )
    }
    println((state as ExampleState.Success).age) // 18
}

But, in my opinion, this would lead to verbose and redundant code. So what solution could you apply to this problem? How can I access my state if its type is ExampleState.Success and always get its updated value?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions