使用 Zustand 插件跟踪状态变化

使用 Zustand 插件跟踪状态变化

使用 Zustand 插件跟踪状态变化

Zustand 是一个非常快速且极简的状态管理解决方案,可用于替代 Redux 等方案。借助我们的 Zustand 插件,你可以在应用程序的整个生命周期中跟踪状态变化。

可以使用以下命令轻松安装该插件:

npm i @openreplay/tracker-zustand

在 Next.js 应用中使用该插件

Section titled 在 Next.js 应用中使用该插件

在本示例中,我们将使用与我们 tracker 的 Next.js 教程中所展示的相同的应用程序,因此如果你想了解所用 Context provider 的更多细节,请先查看那篇教程。

注意:本示例的完整源代码位于这个 Github 仓库 中,如果你需要更多细节,请前往查看。

我所使用的 Context provider 允许对 Tracker 插件进行配置,因此在安装插件之后,我们将修改 _app.tsx 文件,添加下面这条 import 语句:

import trackerZustand from '@openreplay/tracker-zustand'

然后,主 App 函数应该如下所示:

export default function MyApp({ Component, pageProps }: AppProps) {
  const Layout = (Component as any).Layout || Noop

  useEffect(() => {
    document.body.classList?.remove('loading')
  }, [])

  let plugins = [
    {
      fn: trackerZustand,
      name: 'zustand',
    },
  ]

  return (
    <TrackerProvider
      config={{
        plugins
      }}
    >
      <Head />
      <ManagedUIContext>
        <Layout pageProps={pageProps}>
          <Component {...pageProps} />
        </Layout>
      </ManagedUIContext>
    </TrackerProvider>
  )
}

这将自动随 tracker 一起初始化该插件,并且还会使执行插件主函数所返回的数据可供使用。

接下来要做的是创建一个 store,稍后我们会把电商网站上的商品添加到这个 store 中。

使用 Zustand 插件创建 store

Section titled 使用 Zustand 插件创建 store

使用该插件需要你改变通常实例化 Zustand store 的方式,因此可以随意复制粘贴以下代码作为模板:

import create from 'zustand'

const productStore = null
export const useProductsStore = (logger) => {
  if (!productStore) {
    //use a singleton pattern to make sure we only create the store the first time around
    productStore = createProductStore(logger)
  }
  return productStore
}

function createProductStore(logger) {
  return create(
    logger((set) => ({
      products: [],
      addProduct: (p) =>
        set((state) => {
          const prodFound = state.products.find((prod) => {
            return p.id == prod.id
          })
          if (prodFound) {
            p.amount = prodFound.amount + 1
          } else {
            p.amount = 1
          }

          return {
            products: [...state.products.filter((prod) => p.id != prod.id), p],
          }
        }),
    }))
  )
}

导出的函数会在首次使用时创建 store,但之后它只会返回已存在的那个 store。

另一方面,createProductStore 函数会创建 store,设置 addProduct 方法的逻辑,并使用我们提供给它的 logger 函数。你可能已经猜到了,这个 logger 正是我们在使用 Zustand 插件时所得到的那个。

store 准备就绪后,我们就可以开始往里面添加商品了。

向我们的 store 添加商品

Section titled 向我们的 store 添加商品

store 导出了 addProduct 方法,它负责跟踪我们购物车中的商品。

要将该函数导入你的代码中,你需要:

  1. 导入并使用 Tracker 的 context provider。
  2. 从 context 中获取 pluginsReturnedValue 属性。
  3. 使用它来调用 zustand 方法(注意,该方法是以我们上面配置插件时的 name 属性来命名的),并传入一个字符串属性来为 store 命名(该名称将在回放过程中使用)。
  4. zustand 方法将返回 logger,我们会在调用 zustand 的 useProductStore hook(即我们为 store 创建的那个)时使用它。

这一切看起来似乎太复杂了,但本质上都可以归结为:

const { pluginsReturnedValues } = useContext(TrackerContext)

const useProductStoreTracked = useProductsStore(
  pluginsReturnedValues.zustand('products_store')
)
const { addProduct } = useProductStoreTracked()

现在,你只需调用 addProduct 方法,它就会把购物车中的商品添加到 store 中。

你可能已经猜到,如果你想从 Zustand store 中读取数据,需要执行同样的一组步骤。但这次不是取出 addProduct 方法,而是解构 products 数组。

像这样:

const { pluginsReturnedValues } = useContext(TrackerContext)

const useProductStoreTracked = useProductsStore(
  pluginsReturnedValues.zustand('products_store')
)
const { products } = useProductStoreTracked()

然后你就可以像往常一样遍历 products:

<div>
  {products.map((p: Product, idx: Number) => {
    return (
      <div key={+idx}>
        {p.name}({p.amount})
      </div>
    )
  })}
</div>

上面的代码会遍历商品列表并将它们渲染到屏幕上。每当你向 store 添加另一个商品时,这里也会随之更新。

将状态与会话回放同步

Section titled 将状态与会话回放同步

要在回放过程中查看状态变化,只需点击右下角新增的 Zustand 选项卡:

用于 Zustand 跟踪数据的新选项卡

点击它后,你将看到 store 的内容,并能够理解它是如何变化的以及其中保存的数据:

回放过程中显示的状态数据

请注意 store 的名称,它其实就是我们第一次调用 zustand 方法时所使用的那个名称。

如果你使用的不是 Next.js 而是其他框架,使用方式应该是类似的,关键在于你创建 store 的方式。只要你能找到一种在创建 store 时使用 logger 函数的方法,你就能够在回放过程中跟踪状态。

你可以查看这个仓库,获取一个使用 Zustand 插件的可运行 Next.js 应用程序的完整源代码

如果你在项目中设置 Zustand 插件时遇到任何问题,请通过我们的 Slack 社区联系我们,直接向我们的开发人员提问!