How to create private state in stores

How to create private state in stores

Creating Private State in Pinia: Understanding Options vs. Setup Stores and Maintaining Data Integrity

Eduardo San Martin Morote

Eduardo San Martin Morote

March 11, 2024

In Pinia, there is no such thing as private state. This might sound evident in Options Stores, as all the state is exposed and can be accessed from outside the store.

export const useStore = defineStore('store', {
  state: () => ({ message: 'hello' }),
})

const store = useStore()
store.message // 'hello'
store.message = 'hi' // can be modified too
store.message // 'hi'

But if you are using Setup Stores, you might have come across the idea of not returning a property to make it private:

export const useStore = defineStore('store', () => {
  const secret = ref('Never seen outside')

  const censoredSecret = computed(() => '*'.repeat(secret.value.length))

  return { censoredSecret }
})

const store = useStore()
store.secret // undefined
store.censoredSecret // '****************'

This should work, right? Well, not really... Pinia can only scan what we return in a store. This is why you need to return all the state properties, so they can be recognized by Pinia. Why is this necessary? It's needed by a few features:

  • Plugins: Pinia plugins might extend the features of a store. For example, we might want to persist a state property to Local Storage. If we don't return it, the plugin won't be able to access it.
  • Devtools: Pinia Devtools also need to access the state of a store. If we don't return it, we won't be able to interact with it in the devtools.
  • SSR: During Server Side Rendering, the state is serialized and sent to the client from the server. On the client, Pinia will hydrate the state of the store. If we don't return it, it won't be hydrated and therefore will always start as its initial value

So, how can we create private state in Pinia? By creating a new store!

// we don't expose this store but it still picked up by pinia
const usePrivateState = defineStore('store-private', () => {
  const secret = ref('Never seen outside')

  return { secret }
})

export const useStore = defineStore('store', () => {
  // we can just use the store here
  const privateState = usePrivateState()

  const censoredSecret = computed(() => '*'.repeat(privateState.secret.length))

  return { censoredSecret }
})

By creating a new store that isn't exposed to the main app we are keeping its usage to the same file only. But since we are returning secret within that store, it will be picked up by Pinia and we will be able to use it in our other store. In fact, we could even expose this private state store and use it in other stores! Although, there is no need to keep all the private states in one single store, you can create as many as you need.

In practice, creating private state in stores isn't that common, it's more about avoiding mutating the state from outside the store. But there are cases where this is really helpful like avoiding broken states in an application, for such scenarios, this is a great solution to keep in mind. And remember to always return all the state properties in your stores.

The Mastering Pinia Course is Here!

Get a free lesson delivered to your inbox, just click on the button below.

Buy Now