插件 (Plugins)

Pinia-React 支持通过插件来扩展功能。插件是一个在每个 Store 实例创建时应用的函数。你可以:

  • 向 Store 添加新的属性或方法。
  • 增强现有的方法,如 $reset
  • 实现副作用,例如日志记录或本地存储(Local Storage)持久化。

通过在应用入口调用 pinia.use() 将插件添加到你的 Pinia 实例中。

import { createPinia } from 'pinia-react'

// 一个简单的插件,向每个 Store 添加一个静态属性
function CreatedAtPlugin() {
  return { createdAt: new Date() }
}

const pinia = createPinia()
pinia.use(CreatedAtPlugin)

// 在组件或其他文件中...
// const store = useSomeStore()
// console.log(store.createdAt) // Store 创建的时间

简介

Pinia 插件是一个接收 context 对象并可选择性返回要添加到 Store 的属性对象的函数。

context 对象包含三个属性:

  • id: Store 的唯一 ID(传递给 defineStore 的第一个参数)。
  • store: 插件正在增强的 Store 实例。
  • options: 传递给 defineStore() 的选项对象。
export function myPiniaPlugin(context) {
  console.log(`插件已应用到 Store: ${context.id}`)
  // context.store
  // context.options
}

扩展 Store

你可以通过从插件返回对象来向每个 Store 添加属性。这些属性会被合并到 Store 实例中。

pinia.use(() => ({ hello: 'world' }))

你也可以直接在 store 对象上添加属性,这对于需要访问 Store 本身的复杂对象或函数非常有用。

pinia.use(({ store }) => {
  store.customMethod = () => {
    console.log(`Hello from ${store.$id}`)
  }
})

警告: 插件绝不应该直接修改 store.$state(例如 store.$state.newProp = ...)。这样做会绕过响应式系统,导致 UI 无法更新。所有的状态修改都必须通过 Action 或 $patch 进行。

增强 $reset

内置的 $reset() 方法只会重置在 state() 函数中定义的状态。如果插件向 Store 添加了自己的属性,你可能希望 $reset 也能处理它们。正确的方法是增强(或“包装”)原始的 $reset 方法。

下面是一个安全的示例:该插件添加了一个 ephemeralCounter 属性和一个自增方法,并增强了 $reset 以同时重置这个计数器。

function EphemeralCounterPlugin({ store }) {
  // 直接向 Store 实例添加新属性
  store.ephemeralCounter = 0

  // 绑定原始的 $reset 方法
  const originalReset = store.$reset.bind(store)

  return {
    // 添加新方法
    incrementEphemeral() {
      store.ephemeralCounter++
    },
    // 返回一个新的、增强后的 $reset 函数
    $reset() {
      // 首先执行原始的重置逻辑
      originalReset()
      // 然后,重置插件自己的属性
      store.ephemeralCounter = 0
      console.log('临时计数器也已被重置。')
    },
  }
}

pinia.use(EphemeralCounterPlugin)

在插件中调用 $subscribe

你可以在插件中使用 store.$subscribe 来监听状态变化,例如实现本地存储持久化。

pinia.use(({ store, id }) => {
  // 启动时尝试从 localStorage 获取数据
  const savedState = localStorage.getItem(id)
  if (savedState) {
    store.$patch((draft) => {
      Object.assign(draft, JSON.parse(savedState))
    })
  }

  // 订阅变化并保存回去
  store.$subscribe((state) => {
    localStorage.setItem(id, JSON.stringify(state))
  })
})

TypeScript 支持

为插件添加类型

你可以为插件的上下文(context)添加类型注解,以获得更好的类型安全和自动补全。

import { PiniaPluginContext } from 'pinia-react'

export function myPiniaPlugin(context: PiniaPluginContext) {
  // ...
}

为新 Store 属性添加类型

当你通过插件向 Store 添加新属性时,你必须在 PiniaCustomProperties 接口中全局声明它们,以便 TypeScript 能够识别。

import 'pinia-react'

// 确保该文件被视为一个模块
export {}

declare module 'pinia-react' {
  export interface PiniaCustomProperties {
    // 在这里添加插件的属性类型
    createdAt: Date;
    ephemeralCounter: number;
    incrementEphemeral: () => void;
  }
}

现在,你可以在任何 Store 实例上访问这些属性,并享受完整的类型支持。