1

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)
            }
HelloCW
  • 843
  • 22
  • 125
  • 310
  • it is not an appropriate example for derivedstate, as you said both code blocks are equivalent here. – Raji A C Jan 26 '23 at 15:56

2 Answers2

3

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.

Also i answered another practical question here about how to get at which percent first item of LazyColumn is displayed

Thracian
  • 43,021
  • 16
  • 133
  • 222
  • `derivedStateOf` shows an error. It says "Creating a state object during composition without using remember". How to resolve this error? – gaurav kumar Feb 12 '23 at 11:24
  • Put it inside remember block as `remember { derivedStateOf(..)}`. With jetpack compose if you get this type of lint warnings because of accessing states on each composition, using Modifier.composed without any @Composable function click cmd or control + enter to see possible action – Thracian Feb 12 '23 at 11:33
1

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

eric.xiao
  • 11
  • 2
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jul 29 '22 at 10:17