Keep 2 versions of the same composable:Stateless and Stateful
What are stateful and stateless composables in Jetpack compose ?
A stateless component is a component that doesn't maintain any internal state.
It's purely a function of its inputs, meaning that the output of the component is solely determined by the props that are passed to it.
These components are also known as "dumb components" because they don't have any logic beyond rendering what
they're given.
On the other hand, a stateful component is a component that does maintain an internal state.
It's a function of its inputs as well as its own internal state.
Stateful components are also known as "smart components" because they have
logic beyond rendering what they're given. They can respond to user events,
perform complex calculations, and modify their own state, which can trigger a re-render of the component.
An example of a stateful Composable
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Column {
Text("Count: $count")
Button(onClick = { count++ }) {
Text("Increment")
}
}
}
@Composable
fun HomeScreen(
viewModel:HomeScreenViewModel = hiltViewModel(),
) {
val posts = viewModel.posts.collectAsStateWithLifecycle()
LazyColumn(
modifier = Modifier.fillMaxSize()
){
items(items =posts){ post ->
Post(post = post)
}
}
}
In the 1st composable the state is stored in the composable.
This is said to be stateful since the composable stores its current state
In the 2nd composable the state is stored in a viewModel. Normally this is what you
usually do in project
While this approach works fine there are a few issues that arise
They include
Testing: Testing stateful composables can be more difficult because they require you to manage and manipulate their internal state. This can make it harder to write tests that are reliable and cover all possible scenarios.
They break previews: Stateful composables cannot be easily previewed in
development mode since they for them to be initialized the need a customview model instance
which will in turn lead to a custom repository for the view model and custom mock data
for the data sources (local or remote etc)
What is the solution then ?
In comes stateless composables. As discussed earlier stateless composables do not manage state they simply get
state passed on to them. When the UI needs to make changes we can pass them to the necessary functions as parameters
But we will need to eventually instantiate the view model right ?
Yes. We will need 2 different versions of the same composable, A stateless and stateful one then
we use the stateless one for testing and previews.
Here's an example
An example of a stateful Composable
//Stateful version of the composable
@Composable
fun HomeScreen(
viewModel:HomeScreenViewModel = hiltViewModel(),
) {
val posts = viewModel.posts.collectAsStateWithLifeCycle()
HomeScreenContent(posts = posts.value)
}
//Stateless version of the composable
@Composable
fun HomeScreenContent(
posts:List<Post>
) {
LazyColumn(
modifier = Modifier.fillMaxSize()
){
items(items = posts){ post ->
Post(post = post)
}
}
}
Above is the stateful and stateless version of the composable. It a good practice to prefix
the stateless composable with "Content" then pass it to the stateful version and pass the state
and the state modifiers functions for example in this case you could add a like post function and pass its
implementation to the HomeScreenContent composable.
NB: This should only to apply to screen level composables since the other composables in the
screen composable are already statelss
I recently learnt about this new concept and decided to share it
For more examples check how it was implemented in the following repos
Philip Lackner also did a video addressing the same issue
Video Link
I hope this article helped you in a way :)
Top comments (0)