8、状态管理入门:remember 与 mutableStateOf、状态提升的概念

状态管理,说白了就是解决「数据变了,UI 怎么跟着变」的问题。

我记得刚接触 Compose 时,最让我困惑的就是:明明变量改了,界面怎么没反应?后来才明白,Compose 不是靠「监听变量变化」来刷新 UI 的,它有一套自己的规则。今天我们就从最基础的两个工具讲起——remembermutableStateOf,再聊聊「状态提升」这个设计思路。

8.1 为什么需要 remember?

先看一个简单的例子:

@Composable
fun Counter() {
    var count = 0
    Button(onClick = { count++ }) {
        Text("点击了 $count 次")
    }
}

你猜结果会怎样?

点击按钮,count 确实加了 1,但界面上的数字纹丝不动。为什么?

因为 Compose 函数在执行时,var count = 0 会被重新初始化。每次重组(Recomposition),count 都变回 0。你想想看,这就像每次开会都重新发一张白纸,之前写的内容全没了。

这时候就需要 remember 登场了。

核心理解:remember 的作用是「记住」某个值,让它在重组时不被重置。

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }
    Button(onClick = { count++ }) {
        Text("点击了 $count 次")
    }
}

加上 remember 之后,count 的值就能跨重组保留了。点击按钮,数字乖乖地加 1。

嗯,这里要注意:remember 只在当前 Composable 的生命周期内有效。如果这个 Composable 被移出视图树(比如页面跳转),记住的值也就丢了。

8.2 mutableStateOf:让 Compose 知道「值变了」

光记住还不够。你还需要告诉 Compose:「这个值变了,你得刷新 UI」。

mutableStateOf 就是干这个的。它创建了一个「可观察的状态容器」。当容器的值发生变化时,Compose 会自动触发读取过这个状态的 Composable 重组。

我个人习惯把 remembermutableStateOf 搭配使用,像这样:

@Composable
fun GreetingCard() {
    var name by remember { mutableStateOf("") }
    TextField(
        value = name,
        onValueChange = { name = it },
        label = { Text("请输入姓名") }
    )
    Text("你好,$name!")
}

这里 name 变了,Text 会自动更新。整个过程不需要你手动调用任何刷新方法。

小技巧:by 委托可以少写 .value,代码更清爽。如果你看到 val state = remember { mutableStateOf(0) },那后面就得用 state.value 来读写。

8.3 状态提升:把数据「往上挪」

实际项目中,状态不会只在一个 Composable 里自娱自乐。多个组件之间需要共享数据,这时候就引出了「状态提升」的概念。

什么叫状态提升?说白了就是:把状态放到调用方去管理,子组件只负责展示和通知。

我在项目中遇到过这样一个场景:一个搜索框和一个搜索结果列表,它们需要共享同一个关键词。如果各自维护自己的状态,那搜索框输入了内容,列表根本不知道。

正确的做法是:

@Composable
fun SearchScreen() {
    var query by remember { mutableStateOf("") }

    SearchBar(
        query = query,
        onQueryChange = { query = it }
    )
    SearchResults(query = query)
}

@Composable
fun SearchBar(query: String, onQueryChange: (String) -> Unit) {
    TextField(
        value = query,
        onValueChange = onQueryChange,
        label = { Text("搜索") }
    )
}

@Composable
fun SearchResults(query: String) {
    // 根据 query 展示结果
    Text("当前搜索:$query")
}

你看,query 状态被提升到了 SearchScreen 里。SearchBarSearchResults 都不持有状态,它们只通过参数接收数据,通过回调通知变化。

避坑指南:我曾经把一个状态放在子组件里,结果父组件想读取时发现拿不到。后来才意识到,状态提升不只是「技术选择」,更是「架构设计」。原则很简单:谁需要这个状态,谁就持有它。如果多个组件需要,就提升到它们的共同父级。

8.4 状态提升的三大好处

好处 说明
可复用性 子组件不依赖具体状态,换到任何地方都能用
可测试性 状态逻辑集中在父组件,方便单独测试
数据流向清晰 数据从上往下流,事件从下往上流,一眼就能看懂

你想想看,如果每个组件都自己管状态,那调试起来就像在迷宫里找出口。状态提升之后,数据流变成了一条直线,出问题很快就能定位。

8.5 本章知识体系

下面这张图帮你理清今天讲的核心逻辑:

状态管理核心概念 remember 跨重组保留值 mutableStateOf 可观察状态容器 状态提升 状态上移,数据下流 使用场景 • 计数器 • 表单输入 • 开关状态 工作原理 • 读取时订阅 • 修改时通知 • 自动重组 设计原则 • 单一数据源 • 数据向下流 • 事件向上流 三者配合:remember 保活 + mutableStateOf 可观察 + 提升到合适层级

8.6 避坑总结

最后分享几个我踩过的坑:

  • 别在 remember 里放复杂计算——它每次重组都会执行 lambda。如果计算很重,用 remember(keys) 指定依赖。
  • 状态提升别过度——如果一个状态只有子组件自己用,没必要硬提到父级。保持简单。
  • 小心「状态爆炸」——如果一个 Composable 有十几个状态参数,说明该拆分了。我见过一个页面传了 20 多个参数,改起来想哭。

好了,今天的内容就到这里。记住三个关键词:remember 保活、mutableStateOf 可观察、状态提升定层级。把这套组合拳打熟练了,Compose 的状态管理你就入门了。


公众号:蓝海资料掘金营,微信 deep3321