[ Compose ] Detailed Explanation of State Remember

文章目录

          • What State and Remember Does
          • Remember
          • Remember Scope
          • Remember with Key
          • RememberAsSaveable
          • Remember Custom Type Into Bundle
          • Remember Custom Type Into Bundle : Parcelable
          • Remember Custom Type Into Bundle : ListSaver
          • Remember Custom Type Into Bundle : MapSaver
          • DerivedStateOf
          • ProduceState
          • RememberUpdatedState
          • RememberCoroutineScope

What State and Remember Does
  • State : save and update state value

    also can roled as a delegated value provider through remember apis

  • Remember : remember last state value

    if incoming new value equals to last state value, skip recompose

Remember
@Composable
private fun Temp() {
    var count by remember { mutableIntStateOf(1) }
    val onClick = {
        count = Random.nextInt(1, 3)
    }
    Button(onClick = onClick) { Text(count.toString()) }
}

if new random value of count equals to remembered one, Text will skip recomposition

Remember Scope
@Composable
private fun Temp() {
    var count1 by remember { mutableIntStateOf(1) }
    Button(onClick = { count1++ }, Modifier.width(200.dp).height(50.dp)) {
        if (count1 % 3 == 1) {
            var count2 by remember { mutableIntStateOf(1) }
            Text("$count1-$count2", Modifier.clickable { count2++ })
        }
    }
}

remember status will be disposed, when the scope define it removed from composition

when count11, text1-1

when count13, text1-1

because when count1==2, condition is false, composition will be removed

Remember with Key
data class User(val id: String, val name: String)

@Composable
private fun Temp() {
    var id by remember { mutableStateOf("1") }
    var user by remember(id) {
        println("execute computation block")
        mutableStateOf(User("1", "A"))
    }
    Text("${user.name}", Modifier.graphicsLayer { println("recompose") })
    Button(onClick = {
        id = UUID.short()
    }, Modifier.width(100.dp).height(50.dp)) { }
    Button(onClick = {
        user = User("2", "B")
    }, Modifier.width(100.dp).height(50.dp)) { }
}

id is a key for remember, multiple keys is also supported

when key list is same, use latest value in previous state object

when key list is different, recompute a new state object through computation block

RememberAsSaveable

usage of rememberSaveable is exactly same with remember

however it will save value into Bundle in Activity/Fragment

when activity is destroyed, saved value will be restored from Bundle

Remember Custom Type Into Bundle

as Bundle only support primitive types and Parcelable types

there are 3 ways to resolve this problem

  • implement Parcelable interface
  • save and restore by ListSaver
  • save and restore by MapSaver
Remember Custom Type Into Bundle : Parcelable

apply kotlin parcelize plugin

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("org.jetbrains.kotlin.plugin.compose")
    id("org.jetbrains.kotlin.plugin.parcelize")
}

auto implement parcelable interface

@Parcelize
data class User(val id: String, val name: String) : Parcelable

@Composable
private fun Temp() {
    var user by rememberSaveable { mutableStateOf(User("1", "A")) }
    Text("${user.name}")
    Button(onClick = {
        user = User("2", "B")
    }, Modifier.width(100.dp).height(50.dp)) { }
}
Remember Custom Type Into Bundle : ListSaver

the principle is to convert compositive type into multiple primitive types for storage, like ObjectStream in JDK

data class User(val id: String, val name: String)

private val UserListSaver = listSaver<User, Any>(
    save = { listOf(it.id, it.name) },
    restore = {
        val id = it[0] as String
        val name = it[1] as String
        User(id, name)
    }
)

@Composable
private fun Temp() {
    var user by rememberSaveable(stateSaver = UserListSaver) { mutableStateOf(User("1", "A")) }
    Text("${user.name}")
    Button(onClick = {
        user = User("2", "B")
    }, Modifier.width(100.dp).height(50.dp)) { }
}
Remember Custom Type Into Bundle : MapSaver

similarly, MapSaver convert custom type into a map structure

private val UserMapSaver = mapSaver(
    save = { mapOf("id" to it.id, "name" to it.name) },
    restore = {
        val id = it["id"] as String
        val name = it["name"] as String
        User(id, name)
    }
)
DerivedStateOf

create a new state depend on other states

var count1 by remember { mutableIntStateOf(0) }
var count2 by remember { mutableIntStateOf(0) }
val count by remember { derivedStateOf { count1 + count2 } }
ProduceState

create a new state depend on other states

but state is generated asynchronously in a coroutine scope

var foregroundColor by remember { mutableStateOf(Color.Red) }
val backgroundColor by produceState(Color.White) {
    delay(1000L)
    foregroundColor.let {
        value = Color(it.red, it.green, it.blue, 0.1f)
    }
}
RememberUpdatedState

equals to code below, but return value is immutable, value cannot be modified

@Composable
fun <T> rememberUpdatedState(newValue: T): State<T> = remember {
    mutableStateOf(newValue)
}.apply { value = newValue }

value delegated by rememberUpdatedState cannot be modified directly

this is the difference to remember api, its value is always updated by its dependency object

@Composable
private fun Counter(foregroundColor: Color, modifier: Modifier) {
    val foregroundColor by rememberUpdatedState(foregroundColor)
    val backgroundColor = remember(foregroundColor) {
        val origin = foregroundColor
        Color(origin.red, origin.green, origin.blue, 0.2f)
    }
    Box(modifier = modifier.background(backgroundColor)) {
        Text("1", color = foregroundColor)
    }
}

@Preview
@Composable
fun Compose42() {
    var foregroundColor by remember { mutableStateOf(Color.Red) }
    Counter(foregroundColor, Modifier.size(100.dp).clickable { foregroundColor = Color.Blue })
}
RememberCoroutineScope

create a coroutine scope that have a same lifecycle with composable

@Composable
private fun Counter() {
    val coroutineScope = rememberCoroutineScope()
    var count by remember { mutableIntStateOf(0) }
    val onClick: () -> Unit = {
        coroutineScope.launch {
            delay(3000)
            count++
        }
    }
    Button(onClick = onClick) {
        Text("$count")
    }
}

你可能感兴趣的:(android,Kotlin,compose,jetpack,kotlin,adnroid,remember,state)