How can I structure a Composable to only trigger recomposition once?

94 views Asked by At

I'm working on a Composable that updates text after getting an HTTP response, and I'm trying to figure out a way to structure my Composables so that when recomposition occurs, it doesn't repeat the loadJson call.

@Composable
fun Schedule() {
    var textToShow by remember { mutableStateOf("loading...") }

    RemoteConfig.loadJson("schedule.api",
        onSuccess = { routes ->
            textToShow = routes[1].jsonObject.toString()
        },
        onError = { e ->
            textToShow = "Failed to load bus schedule.\n($e)"
        }
    )

    Text(textToShow)
}

I'm using remember to prevent recomposition if the text doesn't change, but loadJson still gets called twice-- once the first time the Composable is drawn, and again when textToShow's value is updated. If the HTTP response was different every time, recomposition would be triggered indefinitely. I believe it boils down to these being in the same scope, but I'm having a hard time restructuring it to do what I want. I'd love to see examples of better approaches.

1

There are 1 answers

0
BenjyTec On BEST ANSWER

You could try to use a LaunchedEffect. It allows executing asynchronous code at the moment that the Composable enters into the composition. It is not called again at recompositions.

You can adjust your code like this:

@Composable
fun Schedule() {
    var textToShow by remember { mutableStateOf("loading...") }

    LaunchedEffect(Unit) { // by providing Unit, it only will be executed once
        // this is a Coroutine Scope, so you could also call suspend functions here
        RemoteConfig.loadJson("schedule.api",
            onSuccess = { routes ->
                textToShow = routes[1].jsonObject.toString()
            },
            onError = { e ->
                textToShow = "Failed to load bus schedule.\n($e)"
            }
        )
    }

    Text(textToShow)
}

As a side note, you could consider to delegate the logic to a ViewModel instead, and only pass the UI state (loading, success, error) to the Composable.