使用 Zustand 插件追踪状态变化

使用 Zustand 插件追踪状态变化

使用 Zustand 插件追踪状态变化

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

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

npm i @openreplay/tracker-zustand

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

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

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

我使用的 Context provider 允许配置 Tracker Plugins,因此在安装插件之后,我们将修改 _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()

然后你就可以像往常一样遍历这些商品:

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

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

将状态与 session replay 同步

Section titled 将状态与 session replay 同步

要在回放期间查看状态变化,只需点击右下角新出现的 Zustand 选项卡:

New tab for the Zustand tracked data

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

State data shown during the replay

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

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

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

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