skip to Main Content

Before Compose, I use LiveData to connect between UI component and data, the UI component will be updated when the data of LiveData type has changed. After I use Compose, I was told that MutableState<T> is just like LiveData, but it is used in Compose.

In the following Code A, val count is MutableState<T> type in Part 1, so the text will be changed in Part 2 when I click the button in Part 3. The above procedure is easy to understand, just like I use LiveData before.

But "3 times clicked" will be displayed when I click the button for 3 times, I don’t understand why Part 4 is launched, you know if (count.value == 3)... is a logic code and it’s not associated with any UI component.

1: Does it mean that the system will observe all code which are associated with val count, and run it automatically when the count.value is changed?

2: Will Part 5 always be relaunched when I click the button in Part 3 ?

Code A

@Composable
fun Counter() {

    Column {
        //Part 1
        val count = remember { mutableStateOf(0) }

       //Part 2
        Text(
            text = "I've been clicked ${count.value} times",
        )

        //Part 3
        Button(onClick = { count.value++ }) {
            Text("Click me")
        }
       
        //Part 4       
        if (count.value == 3) {
            Log.e("My","This is test")
            Toast.makeText(LocalContext.current, "3 times clicked", Toast.LENGTH_LONG).show()
        }

        //Part 5
        Log.e("My","Is it always displayed?")
        Toast.makeText(LocalContext.current, "Is it always displayed", Toast.LENGTH_LONG).show()
    }
}



class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            BasicsCodelabTheme {
                Surface{
                    Counter()
                }
            }
        }
    }
}

Added Content

In Code B, I know the Part A will be relaunched when the value of displayFullItemByID has changed.

  1. Will Part B be relaunched when the value of displayFullItemByID has changed ?

  2. Will Part C be relaunched when the value of displayFullItemByID has changed and it’s equal to 5 ?

Code B

class VoiceAdapters (private val aHomeViewModel: HomeViewModel) {

     fun bind(inputMVoice: MVoice) {                        
           aHomeViewModel.displayFullItemByID.observe(mLifecycleOwner) {
                //Part A
                _isDisplayed.value = inputMVoice.id == it
            }
 
               //Part B
            Log.e("My","Can I relaunched in Part B")
            
            if (aHomeViewModel.displayFullItemByID.value==5) {
                //Part C
                Log.e("My","Can I relaunched in Part C") 
            }
    }

}

class HomeViewModel() : AndroidViewModel(mApplication) {
    private val _displayFullItemByID = MutableLiveData(-1)
    val displayFullItemByID: LiveData<Int> = _displayFullItemByID
    ...
}

2

Answers


  1. A Toast is an example of a side-effect – something that affects the state of the device outside of the Composable itself (in this particular case, having the system show a toast message).

    As per the documentation, compositions should be side-effect free – a core part of the mental model is that the actual composition of a @Composable function could happen many, many times.

    So yes, any reads of count.value will cause that composable function to recompose (i.e., be executed again). However, this probably isn’t what you want in the case of a Toast message – you wouldn’t want multiple toast messages, just the one when the value first changes to 3.

    This is one of the use cases for LaunchedEffect, which only runs its block when the input parameters change:

    // This LaunchedEffect only runs when count.value changes,
    // thus ensuring that only a single call to Toast.makeText()
    // happens for each time the count changes to exactly 3
    LaunchedEffect(count.value) {
        if (count.value == 3) {
            Toast.makeText(LocalContext.current, "3 times clicked", Toast.LENGTH_LONG).show()
        }
    }
    
    Login or Signup to reply.
  2. So let me start with the CODE A

    1. Does it mean that the system will observe all code that is associated with val count, and run it automatically when the
      count.value is changed?

    Answer: If a compose has a local state variable then anytime the state variable is changed then the whole @Composable function gets reinvoked and recomposition happens. Regardless of whether it’s logic code or UI code…

    2: Will Part 5 always be relaunched when I click the button in Part 3?

    Answer: Yes each time when there is a recomposition i.e in this case the change of count causes recomposition… then whole Counter() is called again and hence the part 5 is also called again and again

    Coming to CODE B as mentioned by ianhanniballake it has nothing to do with the Compose and

    only the code within the observe call actually gets called when the
    value changes

    Other Important Things You Should Know :

    You should understand how recomposition works actually from Thinking In Compose Article because you got this question just because of lack of not knowing how recompose actually works…

    one point about recomposition mentioned in that is

    Recomposition skips as many composable functions and lambdas as
    possible.

    An example would be, consider this counter Composable()

        @Composable
        fun Counter() {
            Column {
                Column {
                    val count = remember { mutableStateOf(0) }
                    Text(
                        text = "I've been clicked ${count.value} times",
                    )
                    Button(onClick = { count.value++ }) {
                        Text("Click me")
                    }
                    ShowText("")
                }
            }
        }
    
        @Composable
        fun ShowText(someText: String) {
            Log.d("TAG", "Show Text is Displayed")
            Text(someText)
        }
    

    In the above composable function there is one more Compose() called ShowText().

    Key things to understand in this example code is …

    • When the count value updates the whole Counter() composable function reinvoked and recomposition takes place
    • But, The ShowText() will not be recomposed since it doesn’t depends on any state value/variable from Counter()
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search