State
State is the core of a Store. It's a reactive object that contains the raw data that needs to be shared across your application.
Key Concepts:
- Reactivity: The state is reactive. When data in the State changes, all components that depend on that data will automatically update their views.
- Immutability: Under the hood, state modifications are handled by
immer, which ensures that even though you write "mutating" code, the underlying state is updated immutably.
The state is defined as a function that returns the initial state object.
import { defineStore } from 'pinia-react'
const { useStore, getStore } = defineStore('storeId', {
// An arrow function is recommended for full type inference
state: () => {
return {
count: 0,
name: 'Eduardo',
items: [],
}
},
})
export const useMyStore = useStore
export const getMyStore = getStore
TypeScript
As long as you have TypeScript's strict mode enabled, Pinia-React will automatically infer your state types. However, you might need to help it with initial empty arrays or nullable objects:
interface UserInfo {
name: string
age: number
}
const { useStore, getStore } = defineStore('storeId', {
state: () => {
return {
userList: [] as UserInfo[],
user: null as UserInfo | null,
}
},
})
Alternatively, you can define the state with an interface and type the return value of state():
interface State {
userList: UserInfo[]
user: UserInfo | null
}
const { useStore, getStore } = defineStore('storeId', {
state: (): State => {
return {
userList: [],
user: null,
}
},
})
Accessing State
How you access state depends on the context:
In React Components
Inside a React component or a custom hook, use the useStore hook to get the store instance.
import { useMyStore } from './stores/myStore';
function MyComponent() {
const store = useMyStore();
return <div>Count: {store.count}</div>;
}
Within the Store (in actions or getters)
Inside a store's own actions or getters, use the this keyword to access state and other store properties.
defineStore('storeId', {
state: () => ({ count: 0 }),
actions: {
increment() {
// Use `this` to access state
this.count++;
}
}
})
Resetting State
You can reset the state to its initial value by calling the store's $reset() method.
const store = useStore()
store.$reset()
Changing State with $patch
While actions are the recommended way to change state, the $patch method is useful for batching multiple state mutations together.
The $patch method accepts a single function as its argument. This function receives a draft version of the state, powered by Immer. You can safely "mutate" this draft object, and Immer will produce a new immutable state for you.
store.$patch((draft) => {
draft.count++;
draft.name = 'DIO';
draft.items.push({ name: 'shoes', quantity: 1 });
})
Replacing State
You cannot directly replace the entire $state object. The following will show a warning and will not work:
// This will NOT work
store.$state = { count: 24 }
To achieve a "state replacement" effect, you can use $patch and assign the properties from your new state object to the draft. Object.assign() is a convenient way to do this.
const newState = {
count: 24,
name: 'Eduardo',
items: [{ name: 'shirt', quantity: 2 }]
};
store.$patch((draft) => {
// This will overwrite the draft's properties with the ones from newState
Object.assign(draft, newState);
})
Subscribing to State
You can listen for state changes using the store's $subscribe() method. The callback receives the new state and the previous state as arguments. It returns a function to stop the subscription.
const unsubscribe = cartStore.$subscribe((state, prevState) => {
// Persist the entire state to local storage whenever it changes.
localStorage.setItem('cart', JSON.stringify(state))
})
// To remove the listener, call the returned function
unsubscribe()