Controlled State
Reka UI provides flexible state management for components, allowing developers to use either controlled or uncontrolled state. Understanding when to use each approach ensures better integration with Vue's reactivity system.
Controlled vs. Uncontrolled State
Controlled State
A controlled component receives its state as a prop and requires explicit updates via event listeners. The parent component manages and synchronizes the state.
Example: Controlled SwitchRoot
<script setup>
import { ref } from 'vue'
import { SwitchRoot, SwitchThumb } from 'reka-ui'
const isActive = ref(false)
function handleUpdate(value) {
isActive.value = value
}
</script>
<template>
<SwitchRoot :model-value="isActive" @update:model-value="handleUpdate">
<SwitchThumb />
</SwitchRoot>
</template>
How it works:
- The
SwitchRoot
component’s state is managed by theisActive
ref. - The
@update:modelValue
event ensures updates propagate correctly.
- You need to sync state with Vuex, Pinia, or an API.
- Multiple components rely on the same state.
- You want fine-grained control over updates.
Using v-model with Controlled Components
Vue’s v-model
syntax provides a convenient way to bind values to controlled components in Reka UI. It automatically handles passing the value and listening for updates.
Example: Using v-model
with SwitchRoot
<script setup>
import { ref } from 'vue'
import { SwitchRoot, SwitchThumb } from 'reka-ui'
const isActive = ref(false)
</script>
<template>
<SwitchRoot v-model="isActive">
<SwitchThumb />
</SwitchRoot>
</template>
Uncontrolled State
An uncontrolled component manages its own state internally, without requiring a parent-controlled prop. Instead of modelValue
, Reka UI components use defaultValue
to initialize state.
Example: Uncontrolled SwitchRoot
<template>
<SwitchRoot default-value="true">
<SwitchThumb />
</SwitchRoot>
</template>
How it works:
- The
SwitchRoot
initializes its state withdefaultValue
. - State changes occur internally without external control.
- The component does not need to sync with external logic.
- You want a simpler setup without explicit state management.
- The state is local and does not impact other components.
Common Mistakes & Fixes
1. Forgetting @update:checked
<!-- ❌ Incorrect: -->
<SwitchRoot :modelValue="isActive" />
<!-- ✅ Correct: -->
<SwitchRoot :modelValue="isActive" @update:modelValue="(val) => isActive = val" />
2. Using modelValue
Instead of defaultValue
<!-- ❌ Incorrect: -->
<SwitchRoot :modelValue="true" />
<!-- ✅ Correct: -->
<SwitchRoot defaultValue="true" />
3. Not Providing a Setter for Computed Props
// ❌ Incorrect:
const isActive = computed(() => store.state.toggleState)
// ✅ Correct:
const isActive = computed({
get: () => store.state.toggleState,
set: val => store.commit('setToggleState', val)
})