This episode dissects how to manage Pinia state: direct access, multi-field updates with $patch, resetting with $reset, and reactive destructuring with storeToRefs. You also understand the difference between ordinary destructuring, which kills reactivity, and storeToRefs, which keeps it alive.

State is the heart of a store — the source of truth read by getters, changed by actions, and rendered by components. Now that you understand store anatomy, it's time to understand every way of interacting with state: reading it, changing it, resetting it, and bringing it into components without losing reactivity.
Episode 5 dissects the four main Pinia state management APIs: direct access, $patch, $reset, and storeToRefs. The most common mistake in the Pinia world — ordinary destructuring that leaves the UI never updating — will be dissected thoroughly here.
The simplest way to read and write state is through the store instance:
import { useCounterStore } from '@/stores/counter'
const store = useCounterStore()
console.log(store.count)
store.count = 10Because the store is wrapped in reactive, store.count = 10 immediately triggers an update in any component that reads store.count. For a single simple assignment, this pattern is the clearest.
When you need to update several fields at once, store.$patch is more efficient than many separate assignments. There are two forms: object and function:
store.$patch({ count: 5, title: 'Lima' })
store.$patch((state) => {
state.items.push({ id: 3, name: 'Baru' })
state.count++
})The object form store.$patch({ count: 5 }) suits simple assignments. The function form store.$patch((state) => ...) is needed for operations like pushing to an array that are hard to write as an object literal.
To restore state to its initial values, use $reset():
store.$reset()store.$reset() restores the entire state to the initial values defined in state. It's very useful for a "reset form" button or before a test begins. Note: $reset is only available in Options stores, because Setup stores don't have a single state definition.
This is the part most often misunderstood. Ordinary destructuring breaks the reactivity connection:
// WRONG: reactivity is lost, UI never updates
const { count } = useCounterStore()
count++
// RIGHT: stays reactive
import { storeToRefs } from 'pinia'
const { count } = storeToRefs(useCounterStore())
count.value++const { count } = storeToRefs(useCounterStore()) produces refs, so in the template you write count (auto-unwrap) and in code you write count.value. Ordinary destructuring copies the value at that moment — changing it will never reach the store.
Warning
Remember: storeToRefs only maps state and getters. Actions are plain functions and can still be destructured directly, for example const { increment } = store.
As a quick guide:
store.count directly.storeToRefs(store).$patch.$reset() (Options store).import { useCounterStore } from '@/stores/counter'
import { storeToRefs } from 'pinia'
const store = useCounterStore()
const { count, double } = storeToRefs(store)
const { increment } = storeconst { increment } = store is safe for actions because actions aren't reactive — they stay bound to the same store. Combining these two destructuring lines is the most common pattern in Pinia projects.
Episode 5 closes out the foundations of Pinia state. You can now read and change state directly, perform multi-field updates with $patch, reset with $reset, and do reactive destructuring with storeToRefs without reactivity traps.
Key takeaways:
store.count stays reactive.$patch for updating many fields at once.$patch for array operations.$reset() restores state to its initial values.storeToRefs.storeToRefs only maps state and getters, not actions.In the next episode, episode 6, we'll discuss getters — the store's computed properties for derived state, including accessing another store's state, parameterized getters that return a function, and the memoization behavior you must understand. This will make your templates much cleaner.