在 Jetpack Compose 中,你一定很熟悉 @Composable 函数、remember、MutableState 以及 LaunchedEffect。这些概念深受 React 的启发。理解 React 的 Hooks,你就掌握了函数式组件的灵魂。
1. 状态管理:useState vs remember { mutableStateOf() }
在 Compose 中,状态变化会触发重组 (Recomposition)。在 React 中,状态变化会触发重新渲染 (Re-render)。
Compose
kotlin
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Count: $count")
}
}
React Native
javascript
import React, { useState } from 'react';
import { Button, Text, View } from 'react-native';
export default function Counter() {
// useState 返回一个数组:[当前状态, 更新状态的函数]
const [count, setCount] = useState(0);
return (
<View>
<Text>Count: {count}</Text>
<Button title="Increment" onPress={() => setCount(count + 1)} />
</View>
);
}
关键区别 :React 的组件会在每次状态更新时重新执行整个函数 。useState 保证了在多次执行中,count 能够保持其最新的状态,这与 Compose 的 remember 作用非常类似。
2. 副作用:useEffect vs LaunchedEffect / DisposableEffect
在声明式 UI 中,"副作用" (Side Effect) 指的是那些不属于 UI 渲染逻辑的操作,例如:网络请求、订阅事件、操作定时器等。
在 Compose 中,处理副作用有 LaunchedEffect (挂起函数副作用) 和 DisposableEffect (需要清理的副作用)。 在 React 中,它们被统一为一个大杀器:useEffect。
基本用法
scss
import React, { useState, useEffect } from 'react';
export default function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// 这里的代码在组件挂载后,或者依赖项发生变化时执行
fetchData(userId).then(data => setUser(data));
// 返回一个清理函数 (Cleanup function),相当于 DisposableEffect 的 onDispose
return () => {
cancelFetch();
};
}, [userId]); // 依赖数组 (Dependency Array)
// ...
}
依赖数组陷阱 (The Dependency Array Trap)
useEffect 的第二个参数是依赖数组 ,它是初学者最容易踩坑的地方,也直接对应 Compose LaunchedEffect(key1, key2) 中的 keys。
- 不传依赖数组
useEffect(() => {...}):每次渲染后都会执行!极易导致无限循环和性能问题。 - 传空数组
useEffect(() => {...}, []):只在组件首次挂载 时执行一次(相当于 Android 的onCreate/ Compose 的LaunchedEffect(Unit))。 - 传变量
useEffect(() => {...}, [userId]):在首次挂载,以及userId发生变化后执行。
陷阱警告 :如果你在 useEffect 内部使用了某个组件作用域内的变量(如 state 或 props),你必须将其加入依赖数组中,否则内部拿到的将是旧版本渲染时的闭包值(Stale Closure)。
3. 避免不必要的重渲染:useMemo 与 useCallback
Compose 编译器非常聪明,它会自动跳过参数未变的组合函数的重组。然而,React 默认会在父组件渲染时,重新渲染其所有子组件。
为了优化性能,React 提供了 useMemo (相当于 Compose 的 remember + 计算逻辑) 和 useCallback。
scss
// 缓存计算结果,只有 items 改变时才重新计算
const expensiveResult = useMemo(() => computeExpensive(items), [items]);
// 缓存函数引用,避免每次渲染生成新的回调导致子组件不必要的重渲染
const handleClick = useCallback(() => {
doSomething(count);
}, [count]);
核心总结
React 的函数组件每次渲染都是一次函数的完整执行。useState 保持状态跨越渲染周期,而 useEffect 负责将那些"纯渲染"之外的脏活累活(网络请求、订阅)隔离开来。深入理解"闭包"以及"依赖数组",你就能避开 React 开发中 80% 的奇怪 Bug。