Home > Net >  Why do I need use derivedStateOf in Compose?
Why do I need use derivedStateOf in Compose?

Time:07-27

I think the running result of either Code A or Code B will be the same, why do I need to use derivedStateOf in Code A?

Code A

           var age by remember { mutableStateOf(1) }

            val person by remember {
                derivedStateOf { "my age is $age" }
            }
            Column {
                Button(onClick = {
                    age  = 1
                }) {
                    Text(text = "click add age")
                }
                Text(text = person)
            }

Code B

         var age by remember { mutableStateOf(1) }

            val person= "my age is $age"
            
            Column {
                Button(onClick = {
                    age  = 1
                }) {
                    Text(text = "click add age")
                }
                Text(text = person)
            }

CodePudding user response:

please check this link: Compose: remember() with keys vs. derivedStateOf()

and this one: https://developer.android.com/jetpack/compose/performance#use-derivedstateof

Derived state lets you tell Compose which changes of state actually should trigger recomposition. In this case, specify that you care about when the first visible item changes. When that state value changes, the UI needs to recompose

CodePudding user response:

derivedState is for observing one or multiple States together when specific conditions are met and you use it to not read or trigger recomposition every time State you read changes. Of course you can listen for changes in non-State object but since updating them won't trigger any recompositions it's useless to use derivedState with a non-state object in my opinion. Advantage of using derived state is reading a State that might change in every frame and not hindering performance

Most common example is listening for LazyListState to show Floating action button like in messaging apps.

val showButton by remember {
    derivedStateOf { scrollState.firstVisibleItemIndex != 0 }
}

if (showButton) {
    FloatingActionButton(
        onClick = {
            coroutineScope.launch {
                scrollState.animateScrollToItem(0)
            }
        },
       
    ) {
      // Implementation
    }
}

If you don't use derivedStateOf in this example you might experience lag issues when scrolling.

A simple example

class MyClass() {
    var counter = 0
    var counterState = mutableStateOf(0)
    var stringState = mutableStateOf("")
}


Column(modifier = Modifier.fillMaxSize()) {
    val myObject = remember { MyClass() }
    val counterDerivedState = derivedStateOf { myObject.counter > 5 }
    val counterDerivedStateFromState =
        derivedStateOf { myObject.counterState.value > 5 }

    Button(onClick = { myObject.counter  = 1 }) {
        Text(
            "Counter: ${myObject.counter}"
        )
    }
    Text(
        "Counter: ${myObject.counter} Stateful counter: ${myObject.counterState.value}",
        color = if (counterDerivedState.value) Color.Green else Color.Red
    )

    Spacer(modifier = Modifier.height(20.dp))
    Button(onClick = { myObject.counterState.value  = 1 }) {
        Text(
            "Counter: ${myObject.counterState.value}"
        )
    }

    Text(
        "Counter: ${myObject.counterState.value}",
        color = if (counterDerivedStateFromState.value) Color.Green else Color.Red
    )

    val texAndCounterDerivedState = derivedStateOf {
        myObject.counterState.value > 0 &&
                myObject.stringState.value.isNotEmpty()
    }
    Spacer(modifier = Modifier.height(20.dp))
    TextField(
        value = myObject.stringState.value,
        onValueChange = {
            myObject.stringState.value = it
        }
    )

    Text(
        "Counter: ${myObject.counterState.value}, String: ${myObject.stringState.value}",
        color = if (texAndCounterDerivedState.value) Color.Green else Color.Red
    )

}

We derive states from a non-State variable myObject.counter which doesn't trigger recomposition when its value changes, one from myObject.counterState and combined State of

var counterState = mutableStateOf(0)
var stringState = mutableStateOf("")

If you touch first button you can observe that myObject.counter is increased but since it doesn't trigger a recomposition you don't see it on screen or change of derivedState when it happens.

  • Related