受控状态
如何在 Reka UI 中处理受控状态与非受控状态。
Reka UI 为组件提供了灵活的状态管理,允许开发者使用 受控 或 非受控 状态。了解何时使用每种方法可确保与 Vue 的响应式系统更好地集成。
受控状态 vs. 非受控状态
受控状态
受控 组件将其状态作为 prop 接收,并通过事件监听器进行显式更新。父组件管理并同步状态。
示例:受控 SwitchRoot
<script setup>
import { SwitchRoot, SwitchThumb } from 'reka-ui'
import { ref } from 'vue'
const isActive = ref(false)
function handleUpdate(value) {
isActive.value = value
}
</script>
<template>
<SwitchRoot :model-value="isActive" @update:model-value="handleUpdate">
<SwitchThumb />
</SwitchRoot>
</template>
工作原理:
SwitchRoot组件的状态由isActiveref 管理。@update:modelValue事件确保更新正确传播。
在以下情况下使用受控状态:
- 您需要将状态与
Vuex、Pinia或 API 同步。 - 多个组件依赖于相同的状态。
- 您希望对更新进行细粒度控制。
将 v-model 与受控组件一起使用
Vue 的 v-model 语法提供了一种便捷的方式,可以将值绑定到 Reka UI 中的受控组件。它会自动处理传递值和监听更新。
示例:将 v-model 与 SwitchRoot 一起使用
<script setup>
import { SwitchRoot, SwitchThumb } from 'reka-ui'
import { ref } from 'vue'
const isActive = ref(false)
</script>
<template>
<SwitchRoot v-model="isActive">
<SwitchThumb />
</SwitchRoot>
</template>
非受控状态
非受控 组件在内部管理自己的状态,无需父级控制的 prop。Reka UI 组件使用 defaultValue 而不是 modelValue 来初始化状态。
示例:非受控 SwitchRoot
<template>
<SwitchRoot default-value="true">
<SwitchThumb />
</SwitchRoot>
</template>
工作原理:
SwitchRoot使用defaultValue初始化其状态。- 状态更改在内部发生,无需外部控制。
在以下情况下使用非受控状态:
- 组件不需要与外部逻辑同步。
- 您想要更简单的设置,无需显式状态管理。
- 状态是本地的,不影响其他组件。
常见错误及修复
忘记 @update:modelValue
<SwitchRoot :modelValue="isActive" />
<SwitchRoot :modelValue="isActive" @update:modelValue="(val) => isActive = val" />
使用 modelValue 而不是 defaultValue
<SwitchRoot :modelValue="true" />
<SwitchRoot defaultValue="true" />
未为计算属性提供 Setter
// ❌ 错误:
const isActive = computed(() => store.state.toggleState)
// ✅ 正确:
const isActive = computed({
get: () => store.state.toggleState,
set: val => store.commit('setToggleState', val)
})
计算属性提供 Setter 使用场景,双向绑定 (v-model) 到 Vuex/Pinia Store 的状态:
这是最常见也最具代表性的场景。当您想将一个表单输入或组件(如开关、复选框)通过 v-model 直接绑定到 Vuex 或 Pinia store 中的某个状态时,v-model 会尝试同时读取和写入这个值。
这是最常见也最具代表性的场景。当您想将一个表单输入或组件(如开关、复选框)通过 v-model 直接绑定到 Vuex 或 Pinia store 中的某个状态时,v-model 会尝试同时读取和写入这个值。
<template>
<input type="checkbox" v-model="isActive" />
</template>
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex' // 或者 pinia 的 useStore/defineStore
const store = useStore()
const isActive = computed({
get: () => store.state.toggleState,
set: val => store.commit('setToggleState', val)
})
</script>