--- name: compose-hooks description: | ComposeHooks 项目使用指南 - 为 Jetpack Compose/Compose Multiplatform 提供 React Hooks 风格的状态管理和副作用处理。 使用此技能当需要:(1) 在 Compose 中管理组件状态 (useState, useGetState, useReducer), (2) 处理副作用和生命周期 (useEffect, useMount, useUnmount), (3) 管理网络请求 (useRequest),(4) 使用防抖/节流 (useDebounce, useThrottle), (5) 管理列表/Map状态 (useList, useMap),(6) 使用定时器 (useInterval, useTimeoutFn/useTimeoutPoll/useCountdown), (7) 全局状态管理 (useSelector, useDispatch),(8) 使用无头表格 (useTable/useTableRequest)。 关键词: Compose, Hooks, 状态管理, useRequest, useReducer, useState, useEffect --- # ComposeHooks 使用指南 ComposeHooks 是一个为 Jetpack Compose/Compose Multiplatform 设计的 Hooks 库,灵感来自 React Hooks 和 ahooks。 ## 核心概念 公开的 `useXxx` Hook 均提供对应的 `rememberXxx` 别名,选择你喜欢的命名风格。少数历史 API(如 `Table.useTableInstance`)仅为兼容保留,优先使用顶层 `useXxx`/`rememberXxx`。 ## 平台支持 库以 Kotlin Multiplatform 形式发布(artifact id `hooks2`),覆盖四个目标: | 平台 | target | 说明 | |---|---|---| | Android | `androidTarget` | 含平台专属 hooks(生物识别、网络、电池、传感器等) | | Desktop | `jvm` | 桌面专属 `useKeyPress` | | iOS | `iosArm64` / `iosSimulatorArm64` / `iosX64` | 状态/副作用/网络等通用 hooks | | Web | `wasmJs` (browser) | 自 `2.4.0` 起可用,用于在浏览器中运行 Compose 组件 | **wasmJs 注意事项**: - `commonMain` 中的 hooks 全部可用;平台强相关 hooks(Android 专属、`usePersistent` 的存储后端等)需在各 target 的 actual 实现中处理或降级。 - `useRequest`/`useForm`/`useRedux` 使用的 `KClass` 仅作 Map 键,wasmJs 下可用;真正的反射调用(`.call`/`.callSuspend`/`.createType`)已隔离在 `commonJvmAndroid`,不影响 wasmJs 编译。 - `usePersistent` 采用注入式设计(`SaveToPersistent` 函数类型),wasmJs 下可注入基于 `localStorage` 的实现。 ## 快速参考 ### 状态管理 Hooks | Hook | 用途 | 示例 | |------|------|------| | `useState` | 基础状态管理(推荐用 by 委托);派生状态重载 `useState(keys) { }` 封装 `derivedStateOf` | `var state by useState("")` / `val full by useState(first, last) { "$first $last" }` | | `useStateAsync` | 异步初始化状态 | `val state = useStateAsync { fetchDefault() }` | | `useGetState` | 解构使用的状态管理(推荐) | `val (state, setState, getState) = useGetState(0)` | | `useControllable` | 受控/非受控组件 | `val (state, setValue) = useControllable(default)` | | `useResetState` | 带重置功能的状态 | `val (state, setState, reset) = useResetState("init")` | | `useBoolean` | 布尔状态管理 | `val (state, toggle, set, setTrue, setFalse) = useBoolean(false)` | | `useToggle` | 两值切换 | `val (state, toggle) = useToggle("A", "B")` | | `useToggleEither` | 不同类型切换 | `val (state, toggle) = useToggleEither("left", 100)` | | `useToggleVisible` | 切换内容可见性 | `val (content, toggle) = useToggleVisible { Text("Hi") }` | | `useReducer` | Redux 风格状态管理 | `val (state, dispatch) = useReducer(reducer, initialState)` | | `useRef` | 不触发重组的引用 | `val ref = useRef(0)` | | `useCreation` | 创建复杂对象(类似 useMemo),返回 `Ref` | `val obj by useCreation { ExpensiveObj() }` | | `usePersistent` | 轻量级持久化状态 | `val (state, setState) = usePersistent("key", "default")` | | `usePrevious` | 获取前一个值 | `val prev = usePrevious(state)` | | `useLatestRef` | 始终返回最新值的引用 | `val ref = useLatestRef(state)` | | `useLatestState` | 始终返回最新值的 `State` | `val latest by useLatestState(value)` | | `useLastChanged` | 最后变更时间 | `val time = useLastChanged(source)` | | `useAutoReset` | 自动重置状态 | `var state by useAutoReset("default", 3.seconds)` | ### 集合 Hooks | Hook | 用途 | 示例 | |------|------|------| | `useList` | 列表状态管理 | `val list = useList(1, 2, 3)` | | `useListReduce` | 列表聚合 | `val sum by useListReduce(list) { a, b -> a + b }` | | `useMap` | Map 状态管理 | `val map = useMap("key" to "value")` | | `useImmutableList` | 不可变列表 | `val (list, mutate) = useImmutableList(1, 2, 3)` | | `useImmutableListReduce` | 不可变列表聚合 | `val sum by useImmutableListReduce(list) { a, b -> a + b }` | | `useSorted` | 列表排序 | `val sorted by useSorted(list) { a, b -> a.compareTo(b) }` | | `useCycleList` | 循环列表 | `val (current, index, next, prev, go) = useCycleList(persistentListOf("A","B","C"))` | | `useSelectable` | 选择/多选 | `val (selectedItems, isSelected, toggleSelected) = useSelectable(...)` | ### 数值 Hooks | Hook | 用途 | 示例 | |------|------|------| | `useInt` | Int 状态 | `val count = useInt(0)` | | `useLong` | Long 状态 | `val id = useLong(0L)` | | `useFloat` | Float 状态 | `val ratio = useFloat(0f)` | | `useDouble` | Double 状态 | `val price = useDouble(0.0)` | | `useCounter` | 计数器 | `val (count, inc, dec, set, reset) = useCounter(0)` | ### 副作用 Hooks | Hook | 用途 | 示例 | |------|------|------| | `useEffect` | 副作用处理 | `useEffect(dep) { /* effect */ }` | | `useMount` | 组件挂载时执行 | `useMount { loadData() }` | | `useUnmount` | 组件卸载时执行 | `useUnmount { cleanup() }` | | `useUnmountedRef` | 是否已卸载 | `val unmounted = useUnmountedRef()` | | `useUpdateEffect` | 跳过首次执行的 Effect | `useUpdateEffect(dep) { /* effect */ }` | | `usePausableEffect` | 可暂停/停止的 Effect | `val (stop, pause, resume) = usePausableEffect(dep) { }` | | `useDebounceEffect` | 防抖 Effect | `useDebounceEffect(dep) { /* 500ms后执行 */ }` | | `useThrottleEffect` | 节流 Effect | `useThrottleEffect(dep) { /* 限频执行 */ }` | | `useBackToFrontEffect` | 应用回到前台(Android) | `useBackToFrontEffect { refreshData() }` | | `useFrontToBackEffect` | 应用进入后台(Android) | `useFrontToBackEffect { saveState() }` | ### 防抖与节流 | Hook | 用途 | 示例 | |------|------|------| | `useDebounce` | 防抖值 | `val debounced = useDebounce(value)` | | `useDebounceFn` | 防抖函数 | `val fn = useDebounceFn({ search(it) })` | | `useThrottle` | 节流值 | `val throttled = useThrottle(value)` | | `useThrottleFn` | 节流函数 | `val fn = useThrottleFn({ log(it) })` | ### 定时器与延迟 | Hook | 用途 | 示例 | |------|------|------| | `useInterval` | 定时器 | `useInterval(optionsOf = { period = 1.seconds }) { tick() }` | | `useTimeout` | 延时执行(已废弃,优先用 `useTimeoutFn`) | `useTimeout(3.seconds) { showNotification() }` | | `useTimeoutFn` | 延时执行(可控) | `val (pending, start, stop) = useTimeoutFn(fn, 3.seconds)` | | `useTimeoutPoll` | 超时轮询 | `useTimeoutPoll({ fetchData() }, 5.seconds)` | | `useCountdown` | 倒计时 | `val (remain, formatted) = useCountdown { targetDate = ... }` | ### 时间与日期 | Hook | 用途 | 示例 | |------|------|------| | `useNow` | 当前时间(定时更新) | `val now = useNow { interval = 1.seconds }` | | `useTimestamp` | 时间戳(定时更新) | `val (ts, pause, resume) = useTimestamp { interval = 100.milliseconds }` | | `useTimestampRef` | 时间戳 Ref 版本 | `val (ref, pause, resume) = useTimestampRef { interval = 100.milliseconds }` | | `useDateFormat` | 日期格式化 | `val formatted = useDateFormat(instant, "YYYY-MM-DD")` | | `useTimeAgo` | 相对时间 | `val ago = useTimeAgo(pastInstant)` | ### 数学运算 | Hook | 用途 | 示例 | |------|------|------| | `useAbs` | 绝对值 | `val abs = useAbs(value)` | | `useCeil` | 向上取整 | `val ceil = useCeil(value)` | | `useFloor` | 向下取整 | `val floor = useFloor(value)` | | `useRound` | 四舍五入 | `val round = useRound(value)` | | `useTrunc` | 截断 | `val trunc = useTrunc(value)` | | `useMin/useMax` | 最小/最大值 | `val min = useMin(a, b)` | | `usePow` | 幂运算 | `val pow = usePow(base, exp)` | | `useSqrt` | 平方根 | `val sqrt = useSqrt(value)` | ### 异步 | Hook | 用途 | 示例 | |------|------|------| | `useAsync` | 简化协程 | `val run = useAsync { fetchData() }` | | `useCancelableAsync` | 可取消协程 | `val (run, cancel, isActive) = useCancelableAsync()` | | `useMemoizedFn` | 记忆化递归函数 | `val fn = useMemoizedFn { ... }` | ### 组件通信 | Hook | 用途 | 示例 | |------|------|------| | `useContext` | 跨组件共享状态 | `val theme = useContext(ThemeContext)` | | `useEventSubscribe` | 事件订阅 | `useEventSubscribe { handle(it) }` | | `useEventPublish` | 事件发布 | `val publish = useEventPublish()` | | `useUpdate` | 强制重组 | `val forceUpdate = useUpdate()` | ### 网络请求 | Hook | 用途 | 示例 | |------|------|------| | `useRequest` | 网络请求管理 | `val (data, loading, error, request) = useRequest(requestFn)` | | `useSse` | SSE 流式连接 | `val (data, streaming, error, _, send, cancel) = useSse(streamFn)` | ### 全局状态管理 | Hook | 用途 | 示例 | |------|------|------| | `useSelector` | 从 Store 选择状态 | `val count by useSelector { count }` | | `useDispatch` | 获取 dispatch 函数 | `val dispatch = useDispatch()` | | `useDispatchAsync` | 异步 dispatch | `val dispatchAsync = useDispatchAsync()` | | `useStateMachine` | 状态机 | `val (state, send) = useStateMachine(graph)` | ### 撤销/重做 | Hook | 用途 | 示例 | |------|------|------| | `useUndo` | 撤销/重做 | `val (state, set, reset, undo, redo) = useUndo(initial)` | ### UI 相关 | Hook | 用途 | 示例 | |------|------|------| | `useClipboard` | 剪贴板操作 | `val (copy, paste) = useClipboard()` | | `useKeyboard` | 软键盘显隐控制 | `val (hideKeyboard, showKeyboard) = useKeyboard()` | ### 表单 Hooks | Hook | 用途 | 示例 | |------|------|------| | `Form.useForm` | 表单实例 | `val form = Form.useForm()` | | `Form.useWatch` | 监听表单值 | `val value = Form.useWatch("field", form)` | | `Form.useFormInstance` | 获取表单实例 | `val form = Form.useFormInstance()` | ### 表格 Hooks | Hook | 用途 | 示例 | |------|------|------| | `useTable` | 无头表格 | `val table = useTable(data, columns) { ... }` | | `useTableRequest` | 分页请求表格 | `val tableReq = useTableRequest(requestFn)` | ### 平台专属 Hooks #### Android | Hook | 用途 | 示例 | |------|------|------| | `useBiometric` | 生物识别 | `val (openBiometric, isAuthed) = useBiometric()` | | `useNetwork` | 网络状态 | `val network by useNetwork()` | | `useBatteryInfo` | 电池信息 | `val battery by useBatteryInfo()` | | `useBuildInfo` | 设备信息 | `val build = useBuildInfo()` | | `useScreenInfo` | 屏幕信息 | `val screen = useScreenInfo()` | | `useVibrate` | 振动 | `val (shortVibrate, longVibrate) = useVibrate()` | | `useFlashlight` | 手电筒 | `val (turnOn, turnOff) = useFlashlight()` | | `useWakeLock` | 唤醒锁 | `val (request, release, isActive) = useWakeLock()` | | `useSensor` | 传感器 | `useSensor(Sensor.TYPE_ACCELEROMETER) { event -> ... }` | | `useIlluminance` | 光照强度 | `val illuminance = useIlluminance()` | | `useIdle` | 空闲检测 | `val idle = useIdle()` | | `useScreenBrightness` | 屏幕亮度 | `val (setBrightness, initialBrightness) = useScreenBrightness()` | | `useDisableScreenshot` | 禁用截图 | `val (disable, enable, isDisabled) = useDisableScreenshot()` | | `useWindowFlags` | 窗口标志 | `val (add, clear, isAdded) = useWindowFlags(key, flags)` | #### Desktop | Hook | 用途 | 示例 | |------|------|------| | `useKeyPress` | 键盘按键检测 | `useKeyPress(Key.Enter) { handleEnter() }` | ## 详细参考 - **状态管理 Hooks**: 见 [references/state-hooks.md](references/state-hooks.md) - **副作用 Hooks**: 见 [references/effect-hooks.md](references/effect-hooks.md) - **工具 Hooks**: 见 [references/utility-hooks.md](references/utility-hooks.md) - **网络请求 Hooks**: 见 [references/request-hooks.md](references/request-hooks.md) - **Table 相关 Hooks**: 见 [references/table-hooks.md](references/table-hooks.md) - **表单 Hooks**: 见 [references/form-hooks.md](references/form-hooks.md) - **SSE 流式连接**: 见 [references/sse-hooks.md](references/sse-hooks.md) - **平台专属 Hooks**: 见 [references/platform-hooks.md](references/platform-hooks.md) - **remember 别名索引**: 见 [references/remember-aliases.md](references/remember-aliases.md) ## 常见模式 ### 1. 受控组件 ```kotlin // 推荐:使用 useGetState 解构 val (text, setText) = useGetState("") OutlinedTextField( value = text.value, onValueChange = setText, label = { Text("输入") } ) // 或使用 by 委托 var text by useState("") OutlinedTextField( value = text, onValueChange = { text = it }, label = { Text("输入") } ) ``` ### 2. 解决闭包问题(来自 UseStateExample.kt) ```kotlin // 方式1: 使用 useGetState 函数式更新 val (state, setState) = useGetState("initial") LaunchedEffect(Unit) { repeat(10) { delay(1.seconds) setState { "$it." } // 函数式更新,避免闭包问题 } } // 方式2: 使用 by 委托 var byState by useState("initial") LaunchedEffect(Unit) { repeat(10) { delay(1.seconds) byState += "." // 直接修改,无闭包问题 } } // 方式3: 使用 useLatestRef val (state, setState) = useState("initial") val stateRef = useLatestRef(state) LaunchedEffect(Unit) { repeat(10) { delay(1.seconds) setState("${stateRef.current}.") // 通过 ref 获取最新值 } } ``` ### 3. 网络请求(来自 Auto&Manual.kt) ```kotlin // 自动请求 val (userInfoState, loadingState, errorState) = useRequest( requestFn = { NetApi.userInfo(it) }, optionsOf = { defaultParams = "junerver" // 自动请求必须设置默认参数 } ) val userInfo by userInfoState val loading by loadingState if (loading) { Text(text = "loading ...") } userInfo?.let { Text(text = it.toString()) } // 手动请求 val (repoInfoState, loadingState, errorState, request) = useRequest( requestFn = { it: Tuple2 -> NetApi.repoInfo(it.first, it.second) }, optionsOf = { manual = true defaultParams = tuple("junerver", "ComposeHooks") } ) TButton(text = "request") { request() } ``` ### 4. Redux 风格状态管理(来自 UseReducerExample.kt) ```kotlin // 定义 State 和 Action data class SimpleData(val name: String, val age: Int) sealed interface SimpleAction { data class ChangeName(val newName: String) : SimpleAction data object AgeIncrease : SimpleAction } // 定义 Reducer val simpleReducer: Reducer = { prevState, action -> when (action) { is SimpleAction.ChangeName -> prevState.copy(name = action.newName) is SimpleAction.AgeIncrease -> prevState.copy(age = prevState.age + 1) } } // 使用 val (state, dispatch) = useReducer( simpleReducer, initialState = SimpleData("default", 18), middlewares = arrayOf(logMiddleware()) ) TButton(text = "Change Name") { dispatch(SimpleAction.ChangeName("Alice")) } TButton(text = "Increase Age") { dispatch(SimpleAction.AgeIncrease) } Text(text = "State: ${state.value}") ``` ### 5. 列表操作(来自 UseListExample.kt) ```kotlin val listState = useList(1, 2, 3) // 操作方法 listState.add(4) // 添加 listState.add(0, 0) // 插入 listState.removeAt(0) // 删除 listState.removeLast() // 删除最后一个 listState[0] = 10 // 修改 listState.clear() // 清空 listState.shuffle() // 打乱 // 配合 useListReduce val sum by useListReduce(listState) { a, b -> a + b } ``` ### 6. 防抖输入(来自 UseDebounceExample.kt) ```kotlin var inputValue by useState("") val debouncedValue by useDebounce( value = inputValue, optionsOf = { wait = 500.milliseconds } ) OutlinedTextField( value = inputValue, onValueChange = { inputValue = it }, label = { Text("Type something...") } ) Text(text = "Debounced: $debouncedValue") ``` ### 7. SSE 流式连接 ```kotlin // 自动连接 val (lastEvent, isStreaming, error) = useSse( streamFn = { params: String -> sseService.subscribe(params) }, optionsOf = { defaultParams = "topic-1" onEvent = { event -> println("收到: $event") } } ) // 手动连接 val (lastEvent, isStreaming, error, params, send, cancel) = useSse( streamFn = { url: String -> sseClient.connect(url) }, optionsOf = { manual = true } ) Button(onClick = { send("https://api.example.com/events") }) { Text("开始监听") } ```