Last active
November 14, 2023 22:20
-
-
Save brady-aiello/02fd6bbe152cd7a6746f3bc01d1f32a6 to your computer and use it in GitHub Desktop.
A data state sealed class that treats Loading as a member of every state, rather than an exclusive state itself.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
This UiState model addresses the problems associated with treating Loading as its own state. | |
Loading in general is not an exclusive state. | |
You may be showing data, and fetching (Success + Loading). | |
You might show an error, and try to fetch the data again (Error + Loading) | |
The only time Loading is kind of its own thing is when the screen is first opened, and nothing is displayed yet. | |
So that case, InitialBlankLoading should be a separate state. | |
Here's what a flow in this would look like, fetching a list of messages: | |
1. I open the screen: | |
InitialBlankLoading(loading = true) | |
2. I fetch a list of messages: | |
still InitialBlankLoading(loading = true) | |
3. I get the list of the messages: | |
Success(loading = false) | |
4. I fetch messages again, keeping the previously loaded messages onscreen until it's refreshed: | |
Success(loading = true) | |
5. I receive the updated message list: | |
Success(loading = false) | |
6. I try to fetch the message list again, displaying the old data until it's refreshed: | |
Success(loading = true) | |
7. I get an error, and display the error UI: | |
Error(loading = false) | |
8. I fetch again, keeping the error UI on screen until we get a response: | |
Error(loading = true) | |
9. I receive a correct response, but I've deleted all my messages, so it's just an empty list. | |
Empty(loading = false) | |
The copyLoading() method helps us change state without losing data. | |
We don't want a flash of an empty screen every time we fetch it again. | |
*/ | |
sealed interface UiState<out T: Any> { | |
val loading: Boolean | |
fun copyLoading(loading: Boolean): UiState<T> | |
} | |
/** | |
* We've successfully gotten the data we need for this view. | |
*/ | |
data class Success<out T: Any>( | |
val data: T, | |
override val loading: Boolean | |
) : UiState<T> { | |
override fun copyLoading(loading: Boolean): UiState<T> { | |
return this.copy(loading = loading) | |
} | |
} | |
/** | |
* There was a problem getting the data for this view. | |
*/ | |
data class Error<out T: Any>( | |
val exception: Exception, | |
override val loading: Boolean | |
): UiState<T> { | |
override fun copyLoading(loading: Boolean): UiState<T> { | |
return this.copy(loading = loading) | |
} | |
} | |
/** | |
* We got a response, but there's nothing there, eg. an empty list. | |
*/ | |
data class Empty<out T: Any>( | |
override val loading: Boolean | |
): UiState<T> { | |
override fun copyLoading(loading: Boolean): UiState<T> { | |
return this.copy(loading = loading) | |
} | |
} | |
/** | |
* Only for opening the page for the first time, fetching data before any UI has previously loaded. | |
*/ | |
data class InitialBlankLoading<out T: Any>( | |
override val loading: Boolean = true | |
): UiState<T> { | |
override fun copyLoading(loading: Boolean): UiState<T> { | |
return this.copy(loading = loading) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment