Skip to content

Commit

Permalink
docs: add controlled state
Browse files Browse the repository at this point in the history
  • Loading branch information
zernonia committed Feb 18, 2025
1 parent 9374713 commit 3e7fee0
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ export default defineConfig({
{ text: 'Styling', link: '/docs/guides/styling' },
{ text: 'Animation/Transition', link: '/docs/guides/animation' },
{ text: 'Composition', link: '/docs/guides/composition' },
{ text: `Controlled State ${BadgeHTML('New')}`, link: '/docs/guides/controlled-state' },
{
text: 'Server side rendering',
link: '/docs/guides/server-side-rendering',
Expand Down
2 changes: 1 addition & 1 deletion docs/components/Callout.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const activeType = computed(() => {

<template>
<div
class="text-sm rounded-xl border px-6 py-4 [&_p]:mb-0 my-4"
class="text-sm rounded-xl border px-6 py-4 last:[&>*]:mb-0 my-4"
:class="activeType.class"
>
<div class="flex items-center gap-2 font-semibold mb-4">
Expand Down
132 changes: 132 additions & 0 deletions docs/content/docs/guides/controlled-state.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---
title: Controlled State
description: How to work with controlled vs. uncontrolled state in Reka UI.
---

# Controlled State

<Description>
How to work with controlled vs. uncontrolled state in Reka UI.
</Description>

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`

```vue
<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 the `isActive` ref.
- The `@update:modelValue` event ensures updates propagate correctly.

<Callout type="tip" title="Use controlled state when:">

- 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.

</Callout>

#### 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`

```vue
<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`
```vue
<template>
<SwitchRoot default-value="true">
<SwitchThumb />
</SwitchRoot>
</template>
```

**How it works:**
- The `SwitchRoot` initializes its state with `defaultValue`.
- State changes occur internally without external control.

<Callout type="tip" title="Use uncontrolled state when:">

- 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.

</Callout>

## Common Mistakes & Fixes

### 1. Forgetting `@update:checked`

```vue
<!-- ❌ Incorrect: -->
<SwitchRoot :modelValue="isActive" />
<!-- ✅ Correct: -->
<SwitchRoot :modelValue="isActive" @update:modelValue="(val) => isActive = val" />
```

### 2. Using `modelValue` Instead of `defaultValue`

```vue
<!-- ❌ Incorrect: -->
<SwitchRoot :modelValue="true" />
<!-- ✅ Correct: -->
<SwitchRoot defaultValue="true" />
```

### 3. Not Providing a Setter for Computed Props

```ts
// ❌ Incorrect:
const isActive = computed(() => store.state.toggleState)

// ✅ Correct:
const isActive = computed({
get: () => store.state.toggleState,
set: val => store.commit('setToggleState', val)
})
```

0 comments on commit 3e7fee0

Please sign in to comment.