
作为一名前端开发工程师,在掌握了 Compose 布局系统后,我开始学习 Compose 的状态管理。状态管理是构建响应式用户界面的核心,对于习惯了 React Hooks、Vue 响应式系统的前端开发者来说,Compose 的状态管理既有相似性,也有其独特的设计理念。本文将从前端开发者的视角,学习 Compose 状态管理的核心概念和实际应用。
在前端开发中,我们习惯了这样的状态管理:
// React Hooks
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}// Vue Composition API
function useCounter() {
const count = ref(0);
const increment = () => count.value++;
return { count, increment };
}而在 Compose 中,同样的逻辑变成了:
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Column {
Text("Count: $count")
Button(onClick = { count++ }) {
Text("+")
}
}
}前端开发 | Compose | 说明 |
|---|---|---|
|
| 本地状态管理 |
|
| 副作用处理 |
|
| 函数缓存 |
Props 提升 | State Hoisting | 状态提升 |
Context | CompositionLocal | 跨组件状态传递 |
在深入学习状态概念之前,让我们先运行第三个模块的示例,直观感受 Compose 状态管理的效果:
git clone https://cnb.cool/cool-cc/learn-jetpack-compose
cd learn-jetpack-compose# 运行状态管理示例,在浏览器中查看效果
./gradlew :lesson-03-state-management:wasmJsBrowserDevelopmentRun运行成功后,浏览器会自动打开 http://localhost:8080,效果如下

你将看到:
# 启用热重载,修改代码立即看到效果
./gradlew :lesson-03-state-management:wasmJsBrowserDevelopmentRun --continuous理解 remember 的作用
对于前端开发者来说,remember 是一个需要特别理解的概念。在前端框架中,组件的状态通常由框架自动管理,但在 Compose 中,由于 Composable 函数会在每次重组时重新执行,我们需要 remember 来"记住"状态值:
@Composable
fun WithoutRemember() {
// ❌ 错误:每次重组都会重新创建,状态丢失
var count = mutableStateOf(0)
Button(onClick = { count.value++ }) {
Text("${count.value}") // 永远是 0
}
}
@Composable
fun WithRemember() {
// ✅ 正确:remember 确保状态在重组间保持
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("$count") // 正常递增
}
}前端对比理解:
useState 自动处理状态持久化ref/reactive 由响应式系统管理remember 来保持状态项目中的 BasicStateExamples 展示了基础状态管理的各种用法:
@Composable
fun BasicCounterExample() {
// 类似 React 的 useState(0)
var count by remember { mutableStateOf(0) }
Card {
Column {
Text("当前计数: $count")
Row {
Button(onClick = { count-- }) { Text("-") }
Button(onClick = { count++ }) { Text("+") }
Button(onClick = { count = 0 }) { Text("重置") }
}
}
}
}@Composable
fun TextInputExample() {
var text by remember { mutableStateOf("") }
var isPasswordVisible by remember { mutableStateOf(false) }
Column {
OutlinedTextField(
value = text,
onValueChange = { text = it },
label = { Text("输入文本") }
)
OutlinedTextField(
value = text,
onValueChange = { text = it },
label = { Text("密码") },
visualTransformation = if (isPasswordVisible)
VisualTransformation.None
else
PasswordVisualTransformation(),
trailingIcon = {
IconButton(onClick = { isPasswordVisible = !isPasswordVisible }) {
Icon(
if (isPasswordVisible) Icons.Default.Visibility
else Icons.Default.VisibilityOff,
contentDescription = "切换密码可见性"
)
}
}
)
}
}@Composable
fun SaveableStateExample() {
// 普通状态:配置更改时会丢失
var normalCount by remember { mutableStateOf(0) }
// 可保存状态:配置更改时会保留
var saveableCount by rememberSaveable { mutableStateOf(0) }
Column {
Text("普通状态: $normalCount (配置更改时丢失)")
Text("可保存状态: $saveableCount (配置更改时保留)")
Row {
Button(onClick = { normalCount++ }) { Text("普通 +") }
Button(onClick = { saveableCount++ }) { Text("可保存 +") }
}
}
}项目中的 StateHoistingExamples 展示了状态提升的重要性:
// ❌ 不推荐:状态封装在组件内部
@Composable
fun StatefulCounter() {
var count by remember { mutableStateOf(0) }
Row {
Button(onClick = { count-- }) { Text("-") }
Text("$count")
Button(onClick = { count++ }) { Text("+") }
}
}问题:状态被封装在组件内部,外部无法控制,难以复用和测试。
// ✅ 推荐:状态提升到父组件
@Composable
fun StatelessCounter(
count: Int,
onIncrement: () -> Unit,
onDecrement: () -> Unit
) {
Row {
Button(onClick = onDecrement) { Text("-") }
Text("$count")
Button(onClick = onIncrement) { Text("+") }
}
}
@Composable
fun CounterContainer() {
var count by remember { mutableStateOf(0) }
StatelessCounter(
count = count,
onIncrement = { count++ },
onDecrement = { count-- }
)
}data class Product(
val id: Int,
val name: String,
val price: Double,
val quantity: Int = 0
)
@Composable
fun ShoppingCartExample() {
var products by remember {
mutableStateOf(listOf(
Product(1, "苹果", 5.0),
Product(2, "香蕉", 3.0),
Product(3, "橙子", 4.0)
))
}
Column {
products.forEach { product ->
ProductItem(
product = product,
onQuantityChange = { newQuantity ->
products = products.map {
if (it.id == product.id) it.copy(quantity = newQuantity)
else it
}
}
)
}
val total = products.sumOf { it.price * it.quantity }
Text("总计: ¥${"%.2f".format(total)}")
}
}项目中的 SideEffectExamples 展示了各种副作用处理:
@Composable
fun LaunchedEffectExample() {
var count by remember { mutableStateOf(0) }
var isRunning by remember { mutableStateOf(false) }
// 类似 React 的 useEffect
LaunchedEffect(isRunning) {
if (isRunning) {
repeat(10) { i ->
delay(500)
count = i + 1
}
isRunning = false
}
}
Column {
Text("计数: $count")
Button(
onClick = { isRunning = true },
enabled = !isRunning
) {
Text(if (isRunning) "计数中..." else "开始计数")
}
}
}@Composable
fun DisposableEffectExample() {
var isListening by remember { mutableStateOf(false) }
DisposableEffect(isListening) {
if (isListening) {
// 启动监听器
val listener = createListener()
startListening(listener)
// 清理资源
onDispose {
stopListening(listener)
}
} else {
onDispose { }
}
}
Switch(
checked = isListening,
onCheckedChange = { isListening = it }
)
}@Composable
fun DataLoadingExample() {
var data by remember { mutableStateOf<List<String>?>(null) }
var isLoading by remember { mutableStateOf(false) }
var error by remember { mutableStateOf<String?>(null) }
LaunchedEffect(Unit) {
isLoading = true
try {
delay(2000) // 模拟网络请求
data = listOf("数据1", "数据2", "数据3")
} catch (e: Exception) {
error = e.message
} finally {
isLoading = false
}
}
when {
isLoading -> CircularProgressIndicator()
error != null -> Text("错误: $error", color = Color.Red)
data != null -> {
LazyColumn {
items(data!!) { item ->
Text(item)
}
}
}
}
}@Composable
fun DerivedStateExample() {
var items by remember { mutableStateOf(listOf<String>()) }
// 派生状态:只在依赖项变化时重新计算
val itemCount by remember {
derivedStateOf { items.size }
}
val hasItems by remember {
derivedStateOf { items.isNotEmpty() }
}
Column {
Text("项目数量: $itemCount")
Text("是否有项目: $hasItems")
Button(onClick = { items = items + "新项目${items.size + 1}" }) {
Text("添加项目")
}
}
}@Composable
fun ProduceStateExample() {
val networkData by produceState<String?>(initialValue = null) {
delay(1000)
value = "从网络加载的数据"
}
when (networkData) {
null -> Text("加载中...")
else -> Text("数据: $networkData")
}
}项目中的 DataFlowExamples 展示了单向数据流的概念:
@Composable
fun TaskManagementSystem() {
var tasks by remember { mutableStateOf(listOf<Task>()) }
Column {
// 数据向下流动
TaskInput(
onAddTask = { newTask ->
// 事件向上传递
tasks = tasks + newTask
}
)
TaskList(
tasks = tasks,
onTaskComplete = { taskId ->
// 事件向上传递
tasks = tasks.map {
if (it.id == taskId) it.copy(completed = true)
else it
}
}
)
TaskSummary(tasks = tasks)
}
}Jetpack Compose 的状态管理为前端开发者提供了一套类型安全且强大的响应式编程模型。通过 remember、mutableStateOf、状态提升和副作用处理,我们可以构建出复杂而可维护的用户界面。相比前端框架,Compose 提供了更加严格的类型检查和更清晰的数据流向。
通过这个状态管理学习项目,我们可以理解 Compose 状态管理的核心概念,为后续的 Material3 组件和自定义组件开发打下坚实基础。后续会继续更新基于这个项目的更多内容,敬请期待!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。