HelloCW
HelloCW

Reputation: 2235

Can I use mutableStateOf() instead of produceState in Compose?

The Code A is from the official sample project.

I don't think the produceState is necessary, so I think I can replace Code A with Code B, is it right?

BTW, the Code B can run.

Code A

@Composable
fun DetailsScreen(
    onErrorLoading: () -> Unit,
    modifier: Modifier = Modifier,
    viewModel: DetailsViewModel = viewModel()
) {
    val uiState by produceState(initialValue = DetailsUiState(isLoading = true)) {
        val cityDetailsResult = viewModel.cityDetails
        value = if (cityDetailsResult is Result.Success<ExploreModel>) {
            DetailsUiState(cityDetailsResult.data)
        } else {
            DetailsUiState(throwError = true)
        }
    }

    when {
        uiState.cityDetails != null -> {
            DetailsContent(uiState.cityDetails!!, modifier.fillMaxSize())
        }
        uiState.isLoading -> {
           ...
        }
        else -> { onErrorLoading() }
    }
}

Code B

@Composable
fun DetailsScreen(
    onErrorLoading: () -> Unit,
    modifier: Modifier = Modifier,
    viewModel: DetailsViewModel = viewModel()
) {
    var  uiState by remember {mutableStateOf(DetailsUiState(isLoading = true))}

    val cityDetailsResult = viewModel.cityDetails
    uiState = if (cityDetailsResult is Result.Success<ExploreModel>) {
        DetailsUiState(cityDetailsResult.data)
    } else {
        DetailsUiState(throwError = true)
    }


    when {
        uiState.cityDetails != null -> {
            DetailsContent(uiState.cityDetails!!, modifier.fillMaxSize())
        }
        uiState.isLoading -> {
           ...
        }
        else -> { onErrorLoading() }
    }
}

Upvotes: 1

Views: 984

Answers (1)

Mohammad Sianaki
Mohammad Sianaki

Reputation: 1731

Let's take a look at productState under the hood:

@Composable
fun <T> produceState(
    initialValue: T,
    @BuilderInference producer: suspend ProduceStateScope<T>.() -> Unit
): State<T> {
    val result = remember { mutableStateOf(initialValue) }
    LaunchedEffect(Unit) {
        ProduceStateScopeImpl(result, coroutineContext).producer()
    }
    return result
}

productState without a key in it's arguments, uses LaunchedEffect with Unit key which Create an effect that matches the lifecycle of the call site.

It means if DetailsScreen recomposes, the code that provides uiState won't start again.

But In code B, you are just remembering DetailsUiState across recomposition, and below line will be executed in every recomposition.

val cityDetailsResult = viewModel.cityDetails
uiState = if (cityDetailsResult is Result.Success<ExploreModel>) {
    DetailsUiState(cityDetailsResult.data)
} else {
    DetailsUiState(throwError = true)
}

Upvotes: 1

Related Questions