Skip to content

persist

persist 中间件允许你在页面重新加载或应用重新启动时保留存储的状态。

¥persist middleware lets you persist a store's state across page reloads or application restarts.

js
const nextStateCreatorFn = persist(stateCreatorFn, persistOptions)

类型

¥Types

签名

¥Signature

ts
persist<T, U>(stateCreatorFn: StateCreator<T, [], []>, persistOptions?: PersistOptions<T, U>): StateCreator<T, [['zustand/persist', U]], []>

变量

¥Mutator

ts
['zustand/persist', U]

参考

¥Reference

persist(stateCreatorFn)

参数

¥Parameters

  • stateCreatorFn:以 set 函数、get 函数和 store 作为参数的函数。通常,你将返回一个包含要公开的方法的对象。

    ¥stateCreatorFn: A function that takes set function, get function and store as arguments. Usually, you will return an object with the methods you want to expose.

  • persistOptions:定义存储选项的对象。

    ¥persistOptions: An object to define storage options.

    • name:存储中存储项目的唯一名称。

      ¥name: A unique name of the item for your store in the storage.

    • 可选 storage:默认为 createJSONStorage(() => localStorage)。*

      ¥optional storage: Defaults to createJSONStorage(() => localStorage). -

    • 可选 partialize:在持久化之前过滤状态字段的函数。

      ¥optional partialize: A function to filter state fields before persisting it.

    • 可选 onRehydrateStorage:返回允许在状态补液之前和之后自定义逻辑的函数或函数。

      ¥optional onRehydrateStorage: A function or function returning a function that allows custom logic before and after state rehydration.

    • 可选 version:持久状态的版本号。如果存储的状态版本不匹配,则不会使用它。

      ¥optional version: A version number for the persisted state. If the stored state version doesn't match, it won't be used.

    • 可选 migrate:如果发生版本不匹配,则迁移持久状态的函数。

      ¥optional migrate: A function to migrate persisted state if the version mismatch occurs.

    • 可选 merge:在补液期间将持久状态与当前状态合并时的自定义逻辑函数。默认为浅合并。

      ¥optional merge: A function for custom logic when merging persisted state with the current state during rehydration. Defaults to a shallow merge.

    • 可选 skipHydration:默认为 false。如果是 true,中间件将不会在初始化时自动重新补充状态。在这种情况下手动使用 rehydrate 函数。这对于服务器端渲染 (SSR) 应用很有用。

      ¥optional skipHydration: Defaults to false. If true, the middleware won't automatically rehydrate the state on initialization. Use rehydrate function manually in this case. This is useful for server-side rendering (SSR) applications.

返回

¥Returns

persist 返回状态创建器函数。

¥persist returns a state creator function.

用法

¥Usage

持久化状态

¥Persisting a state

在本教程中,我们将使用 vanilla 存储和 persist 中间件创建一个简单的位置跟踪器。示例跟踪鼠标在容器内移动时的 position,并将 position 存储在本地存储中,因此即使页面重新加载,它也会持续存在。

¥In this tutorial, we'll create a simple position tracker using vanilla store and the persist middleware. The example tracks the position of the mouse as it moves within a container and stores the position in local storage, so it persists even when the page reloads.

我们首先设置一个保存位置(具有 xy 坐标的对象)的 vanilla 存储和一个更新它的操作。我们还将使用 persist 中间件将位置存储在 localStorage 中。

¥We start by setting up a vanilla store that holds the position (an object with x and y coordinates) and an action to update it. We'll also use the persist middleware to store the position in localStorage.

ts
import { createStore } from 'zustand/vanilla'
import { persist } from 'zustand/middleware'

type PositionStoreState = { position: { x: number; y: number } }

type PositionStoreActions = {
  setPosition: (nextPosition: PositionStoreState['position']) => void
}

type PositionStore = PositionStoreState & PositionStoreActions

const positionStore = createStore<PositionStore>()(
  persist(
    (set) => ({
      position: { x: 0, y: 0 },
      setPosition: (position) => set({ position }),
    }),
    { name: 'position-storage' },
  ),
)

接下来,我们将跟踪 div 内的鼠标移动并使用新位置更新存储。

¥Next, we'll track the mouse movements inside a div and update the store with the new position.

ts
const $dotContainer = document.getElementById('dot-container') as HTMLDivElement
const $dot = document.getElementById('dot') as HTMLDivElement

$dotContainer.addEventListener('pointermove', (event) => {
  positionStore.getState().setPosition({
    x: event.clientX,
    y: event.clientY,
  })
})

我们希望通过将 div 元素(代表点)移动到新坐标来反映屏幕上的位置更新。

¥We want to reflect the position updates on the screen by moving a div element (representing the dot) to the new coordinates.

ts
const render: Parameters<typeof positionStore.subscribe>[0] = (state) => {
  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`
}

render(positionStore.getState(), positionStore.getState())

positionStore.subscribe(render)

这是完整的代码。

¥Here’s the complete code.

ts
import { createStore } from 'zustand/vanilla'
import { persist } from 'zustand/middleware'

type PositionStoreState = { position: { x: number; y: number } }

type PositionStoreActions = {
  setPosition: (nextPosition: PositionStoreState['position']) => void
}

type PositionStore = PositionStoreState & PositionStoreActions

const positionStore = createStore<PositionStore>()(
  persist(
    (set) => ({
      position: { x: 0, y: 0 },
      setPosition: (position) => set({ position }),
    }),
    { name: 'position-storage' },
  ),
)

const $dotContainer = document.getElementById('dot-container') as HTMLDivElement
const $dot = document.getElementById('dot') as HTMLDivElement

$dotContainer.addEventListener('pointermove', (event) => {
  positionStore.getState().setPosition({
    x: event.clientX,
    y: event.clientY,
  })
})

const render: Parameters<typeof positionStore.subscribe>[0] = (state) => {
  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`
}

render(positionStore.getState(), positionStore.getState())

positionStore.subscribe(render)

这是 html 代码

¥Here's the html code

html
<div
  id="dot-container"
  style="position: relative; width: 100vw; height: 100vh;"
>
  <div
    id="dot"
    style="position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;"
  ></div>
</div>

部分保持状态

¥Persisting a state partially

在本教程中,我们将使用 vanilla 存储和 persist 中间件创建一个简单的位置跟踪器。此外,我们将向你展示如何仅保留部分状态(部分持久性),这在你不想将整个状态存储在 localStorage 中时很有用。

¥In this tutorial, we'll create a simple position tracker using vanilla store and the persist middleware. Additionally, we'll show you how to persist only part of the state (partial persistence), which can be useful when you don’t want to store the entire state in localStorage.

我们将首先创建一个原始存储,用于保存位置状态和更新它的操作。我们将使用 persist 中间件仅保留状态的相关部分(在本例中为包含位置的上下文)。

¥We’ll first create a vanilla store that holds the position state and actions to update it. We'll use the persist middleware to persist only the relevant part of the state (in this case, the context containing the position).

ts
import { createStore } from 'zustand/vanilla'
import { persist } from 'zustand/middleware'

type PositionStoreState = {
  context: {
    position: { x: number; y: number }
  }
}

type PositionStoreActions = {
  actions: {
    setPosition: (
      nextPosition: PositionStoreState['context']['position'],
    ) => void
  }
}

type PositionStore = PositionStoreState & PositionStoreActions

const positionStore = createStore<PositionStore>()(
  persist(
    (set) => ({
      context: {
        position: { x: 0, y: 0 },
      },
      actions: {
        setPosition: (position) => set({ context: { position } }),
      },
    }),
    {
      name: 'position-storage',
      partialize: (state) => ({ context: state.context }),
    },
  ),
)

接下来,我们将跟踪 div 内的鼠标移动并使用新位置更新存储。

¥Next, we'll track the mouse movements inside a div and update the store with the new position.

ts
const $dotContainer = document.getElementById('dot-container') as HTMLDivElement
const $dot = document.getElementById('dot') as HTMLDivElement

$dotContainer.addEventListener('pointermove', (event) => {
  positionStore.getState().actions.setPosition({
    x: event.clientX,
    y: event.clientY,
  })
})

我们希望通过将 div 元素(代表点)移动到新坐标来反映屏幕上的位置更新。

¥We want to reflect the position updates on the screen by moving a div element (representing the dot) to the new coordinates.

ts
const render: Parameters<typeof positionStore.subscribe>[0] = (state) => {
  $dot.style.transform = `translate(${state.context.position.x}px, ${state.context.position.y}px)`
}

render(positionStore.getState(), positionStore.getState())

positionStore.subscribe(render)

以下是创建一个点的完整代码,该点跟随容器内的鼠标移动,并在 localStorage 中保留 context

¥Here’s the full code to create a dot that follows your mouse movement inside a container and persists the context in localStorage.

ts
import { createStore } from 'zustand/vanilla'
import { persist } from 'zustand/middleware'

type PositionStoreState = {
  context: {
    position: { x: number; y: number }
  }
}

type PositionStoreActions = {
  actions: {
    setPosition: (
      nextPosition: PositionStoreState['context']['position'],
    ) => void
  }
}

type PositionStore = PositionStoreState & PositionStoreActions

const positionStore = createStore<PositionStore>()(
  persist(
    (set) => ({
      context: {
        position: { x: 0, y: 0 },
      },
      actions: {
        setPosition: (position) => set({ context: { position } }),
      },
    }),
    {
      name: 'position-storage',
      partialize: (state) => ({ context: state.context }),
    },
  ),
)

const $dotContainer = document.getElementById('dot-container') as HTMLDivElement
const $dot = document.getElementById('dot') as HTMLDivElement

$dotContainer.addEventListener('pointermove', (event) => {
  positionStore.getState().actions.setPosition({
    x: event.clientX,
    y: event.clientY,
  })
})

const render: Parameters<typeof positionStore.subscribe>[0] = (state) => {
  $dot.style.transform = `translate(${state.context.position.x}px, ${state.context.position.y}px)`
}

render(positionStore.getState(), positionStore.getState())

positionStore.subscribe(render)

这是 html 代码

¥Here's the html code

html
<div
  id="dot-container"
  style="position: relative; width: 100vw; height: 100vh;"
>
  <div
    id="dot"
    style="position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;"
  ></div>
</div>

使用自定义存储保持状态

¥Persisting a state with custom storage

在此迷你教程中,我们将使用 vanilla 存储创建一个简单的位置跟踪系统,其中位置状态保留在 URL 的搜索参数中。这种方法允许直接在浏览器的 URL 中保持状态,这对于在页面重新加载时维护状态或共享嵌入状态的链接非常有用。

¥In this mini tutorial, we’ll create a simple position-tracking system using vanilla store, where the position state is persisted in the URL's search parameters. This approach allows state persistence directly in the browser's URL, which can be useful for maintaining state across page reloads or sharing links with state embedded.

我们需要实现函数来操作 URL 搜索参数,就好像它们是一种存储机制一样。这包括检索、设置和删除参数。

¥We need to implement functions to manipulate URL search parameters as if they were a storage mechanism. This includes retrieving, setting, and removing parameters.

ts
const getSearchParams = () => {
  return new URL(location.href).searchParams
}

const updateSearchParams = (searchParams: URLSearchParams) => {
  window.history.replaceState(
    {},
    '',
    `${location.pathname}?${searchParams.toString()}`,
  )
}

const getSearchParam = (key: string) => {
  const searchParams = getSearchParams()
  return searchParams.get(key)
}

const updateSearchParam = (key: string, value: string) => {
  const searchParams = getSearchParams()
  searchParams.set(key, value)

  updateSearchParams(searchParams)
}

const removeSearchParam = (key: string) => {
  const searchParams = getSearchParams()
  searchParams.delete(key)

  updateSearchParams(searchParams)
}

要将 URL 搜索参数用作存储,我们定义了一个具有 getItemsetItemremoveItem 方法的 searchParamsStorage 对象。这些方法映射到我们操纵搜索参数的自定义函数。

¥To use the URL search parameters as storage, we define a searchParamsStorage object with getItem, setItem, and removeItem methods. These methods map to our custom functions that manipulate search parameters.

ts
const searchParamsStorage = {
  getItem: (key: string) => getSearchParam(key),
  setItem: (key: string, value: string) => updateSearchParam(key, value),
  removeItem: (key: string) => removeSearchParam(key),
}

现在,我们使用 persist 中间件初始化原始存储,指定我们要使用自定义存储。我们将在 URL 搜索参数中保留位置数据,而不是默认的 localStoragesessionStorage

¥Now, we initialize the vanilla store using the persist middleware, specifying that we want to use our custom storage. Instead of the default localStorage or sessionStorage, we’ll persist the position data in the URL search parameters.

ts
import { createStore } from 'zustand/vanilla'
import { persist, createJSONStorage } from 'zustand/middleware'

type PositionStoreState = { position: { x: number; y: number } }

type PositionStoreActions = {
  setPosition: (nextPosition: PositionStoreState['position']) => void
}

type PositionStore = PositionStoreState & PositionStoreActions

const positionStore = createStore<PositionStore>()(
  persist(
    (set) => ({
      position: { x: 0, y: 0 },
      setPosition: (position) => set({ position }),
    }),
    {
      name: 'position-storage',
      storage: createJSONStorage(() => searchParamsStorage),
    },
  ),
)

接下来,我们将跟踪 div 内的鼠标移动并使用新位置更新存储。

¥Next, we'll track the mouse movements inside a div and update the store with the new position.

ts
const $dotContainer = document.getElementById('dot-container') as HTMLDivElement
const $dot = document.getElementById('dot') as HTMLDivElement

$dotContainer.addEventListener('pointermove', (event) => {
  positionStore.getState().setPosition({
    x: event.clientX,
    y: event.clientY,
  })
})

我们希望通过将 div 元素(代表点)移动到新坐标来反映屏幕上的位置更新。

¥We want to reflect the position updates on the screen by moving a div element (representing the dot) to the new coordinates.

ts
const render: Parameters<typeof positionStore.subscribe>[0] = (state) => {
  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`
}

render(positionStore.getState(), positionStore.getState())

positionStore.subscribe(render)

以下是创建一个点的完整代码,该点跟随容器内的鼠标移动,并在 URL 的搜索参数中保留位置。

¥Here’s the full code to create a dot that follows your mouse movement inside a container and persists the position in URL's search parameters.

ts
import { createStore } from 'zustand/vanilla'
import { persist, createJSONStorage } from 'zustand/middleware'

type PositionStoreState = { position: { x: number; y: number } }

type PositionStoreActions = {
  setPosition: (nextPosition: PositionStoreState['position']) => void
}

type PositionStore = PositionStoreState & PositionStoreActions

const getSearchParams = () => {
  return new URL(location.href).searchParams
}

const updateSearchParams = (searchParams: URLSearchParams) => {
  window.history.replaceState(
    {},
    '',
    `${location.pathname}?${searchParams.toString()}`,
  )
}

const getSearchParam = (key: string) => {
  const searchParams = getSearchParams()
  return searchParams.get(key)
}

const updateSearchParam = (key: string, value: string) => {
  const searchParams = getSearchParams()
  searchParams.set(key, value)

  updateSearchParams(searchParams)
}

const removeSearchParam = (key: string) => {
  const searchParams = getSearchParams()
  searchParams.delete(key)

  updateSearchParams(searchParams)
}

const searchParamsStorage = {
  getItem: (key: string) => getSearchParam(key),
  setItem: (key: string, value: string) => updateSearchParam(key, value),
  removeItem: (key: string) => removeSearchParam(key),
}

const positionStore = createStore<PositionStore>()(
  persist(
    (set) => ({
      position: { x: 0, y: 0 },
      setPosition: (position) => set({ position }),
    }),
    {
      name: 'position-storage',
      storage: createJSONStorage(() => searchParamsStorage),
    },
  ),
)

const $dotContainer = document.getElementById('dot-container') as HTMLDivElement
const $dot = document.getElementById('dot') as HTMLDivElement

$dotContainer.addEventListener('pointermove', (event) => {
  positionStore.getState().setPosition({
    x: event.clientX,
    y: event.clientY,
  })
})

const render: Parameters<typeof positionStore.subscribe>[0] = (state) => {
  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`
}

render(positionStore.getState(), positionStore.getState())

positionStore.subscribe(render)

这是 html 代码

¥Here's the html code

html
<div
  id="dot-container"
  style="position: relative; width: 100vw; height: 100vh;"
>
  <div
    id="dot"
    style="position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;"
  ></div>
</div>

通过版本控制和迁移保持状态

¥Persisting a state through versioning and migrations

在本教程中,我们将探索如何使用版本控制和迁移来管理状态持久性。我们将演示如何在不破坏现有持久数据的情况下跨版本发展状态模式。

¥In this tutorial, we’ll explore how to manage state persistence using versioning and migration. We will demonstrate how to evolve your state schema across versions without breaking existing persisted data.

在转向版本化状态管理之前,我们模拟了 version 0 的初始状态。这是通过在 localStorage 中手动设置 version 0 状态(如果它尚不存在)来完成的。version 0 状态将坐标保存为 xy 字段。

¥Before moving to versioned state management, we simulate an initial state for version 0. This is done by manually setting a version 0 state in localStorage if it doesn't already exist. The version 0 state saves the coordinates as x and y fields.

ts
// For tutorial purposes only
if (!localStorage.getItem('position-storage')) {
  localStorage.setItem(
    'position-storage',
    JSON.stringify({
      state: { x: 100, y: 100 }, // version 0 structure
      version: 0,
    }),
  )
}

接下来,我们使用 persist 中间件来处理状态持久性。我们还添加了一个迁移函数来处理版本之间的更改。在此示例中,我们将状态从 version 0(其中 xy 是分开的) migrateversion 1,其中它们组合成 position 对象。

¥Next, we use persist middleware to handle state persistence. We also add a migration function to handle changes between versions. In this example, we migrate the state from version 0 (where x and y are separate) to version 1, where they are combined into a position object.

ts
migrate: (persisted: any, version) => {
  if (version === 0) {
    persisted.position = { x: persisted.x, y: persisted.y }
    delete persisted.x
    delete persisted.y
  }

  return persisted
}

接下来,我们将跟踪 div 内的鼠标移动并使用新位置更新存储。

¥Next, we'll track the mouse movements inside a div and update the store with the new position.

ts
const $dotContainer = document.getElementById('dot-container') as HTMLDivElement
const $dot = document.getElementById('dot') as HTMLDivElement

$dotContainer.addEventListener('pointermove', (event) => {
  positionStore.getState().setPosition({
    x: event.clientX,
    y: event.clientY,
  })
})

我们希望通过将 div 元素(代表点)移动到新坐标来反映屏幕上的位置更新。

¥We want to reflect the position updates on the screen by moving a div element (representing the dot) to the new coordinates.

ts
const render: Parameters<typeof positionStore.subscribe>[0] = (state) => {
  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`
}

render(positionStore.getState(), positionStore.getState())

positionStore.subscribe(render)

这是完整的代码。

¥Here’s the complete code.

ts
import { createStore } from 'zustand/vanilla'
import { persist } from 'zustand/middleware'

// For tutorial purposes only
if (!localStorage.getItem('position-storage')) {
  localStorage.setItem(
    'position-storage',
    JSON.stringify({
      state: { x: 100, y: 100 },
      version: 0,
    }),
  )
}

type PositionStoreState = { position: { x: number; y: number } }

type PositionStoreActions = {
  setPosition: (nextPosition: PositionStoreState['position']) => void
}

type PositionStore = PositionStoreState & PositionStoreActions

const positionStore = createStore<PositionStore>()(
  persist(
    (set) => ({
      position: { x: 0, y: 0 }, // version 0: just x: 0, y: 0
      setPosition: (position) => set({ position }),
    }),
    {
      name: 'position-storage',
      version: 1,
      migrate: (persisted: any, version) => {
        if (version === 0) {
          persisted.position = { x: persisted.x, y: persisted.y }
          delete persisted.x
          delete persisted.y
        }

        return persisted
      },
    },
  ),
)

const $dotContainer = document.getElementById('dot-container') as HTMLDivElement
const $dot = document.getElementById('dot') as HTMLDivElement

$dotContainer.addEventListener('pointermove', (event) => {
  positionStore.getState().setPosition({
    x: event.clientX,
    y: event.clientY,
  })
})

const render: Parameters<typeof positionStore.subscribe>[0] = (state) => {
  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`
}

render(positionStore.getState(), positionStore.getState())

positionStore.subscribe(render)

这是 html 代码

¥Here's the html code

html
<div
  id="dot-container"
  style="position: relative; width: 100vw; height: 100vh;"
>
  <div
    id="dot"
    style="position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;"
  ></div>
</div>

使用嵌套对象保持状态

¥Persisting a state with nested objects

在本教程中,我们将创建一个 vanilla 存储,用于跟踪由 xy 坐标表示的位置。我们还将使用 localStorage 实现持久性,并演示如何处理可能缺少字段的状态合并。

¥In this tutorial, we’ll create a vanilla store that keeps track of a position represented by x and y coordinates. We will also implement persistence using localStorage and demonstrate how to handle merging of state with potentially missing fields.

为了模拟教程的初始状态,我们将检查我们的位置数据是否存在于 localStorage 中。如果没有,我们将进行设置。

¥To simulate an initial state for the tutorial, we will check if our position data exists in localStorage. If it doesn't, we’ll set it up.

ts
if (!localStorage.getItem('position-storage')) {
  localStorage.setItem(
    'position-storage',
    JSON.stringify({
      state: { position: { y: 100 } }, // missing `x` field
      version: 0,
    }),
  )
}

现在,我们将创建存储并将其配置为使用持久性和深度合并。

¥Now, we will create the store and configure it to use persistence and deep merging.

ts
import { createStore } from 'zustand/vanilla'
import { persist } from 'zustand/middleware'
import createDeepMerge from '@fastify/deepmerge'

const deepMerge = createDeepMerge({ all: true })

type PositionStoreState = { position: { x: number; y: number } }

type PositionStoreActions = {
  setPosition: (nextPosition: PositionStoreState['position']) => void
}

type PositionStore = PositionStoreState & PositionStoreActions

const positionStore = createStore<PositionStore>()(
  persist(
    (set) => ({
      position: { x: 0, y: 0 },
      setPosition: (position) => set({ position }),
    }),
    {
      name: 'position-storage',
      merge: (persisted, current) => deepMerge(current, persisted) as never,
    },
  ),
)

接下来,我们将跟踪 div 内的鼠标移动并使用新位置更新存储。

¥Next, we'll track the mouse movements inside a div and update the store with the new position.

ts
const $dotContainer = document.getElementById('dot-container') as HTMLDivElement
const $dot = document.getElementById('dot') as HTMLDivElement

$dotContainer.addEventListener('pointermove', (event) => {
  positionStore.getState().setPosition({
    x: event.clientX,
    y: event.clientY,
  })
})

我们希望通过将 div 元素(代表点)移动到新坐标来反映屏幕上的位置更新。

¥We want to reflect the position updates on the screen by moving a div element (representing the dot) to the new coordinates.

ts
const render: Parameters<typeof positionStore.subscribe>[0] = (state) => {
  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`
}

render(positionStore.getState(), positionStore.getState())

positionStore.subscribe(render)

这是完整的代码。

¥Here’s the complete code.

ts
import { createStore } from 'zustand/vanilla'
import { persist } from 'zustand/middleware'
import createDeepMerge from '@fastify/deepmerge'

const deepMerge = createDeepMerge({ all: true })

// For tutorial purposes only
if (!localStorage.getItem('position-storage')) {
  localStorage.setItem(
    'position-storage',
    JSON.stringify({
      state: { position: { y: 100 } }, // missing `x` field
      version: 0,
    }),
  )
}

type PositionStoreState = { position: { x: number; y: number } }

type PositionStoreActions = {
  setPosition: (nextPosition: PositionStoreState['position']) => void
}

type PositionStore = PositionStoreState & PositionStoreActions

const positionStore = createStore<PositionStore>()(
  persist(
    (set) => ({
      position: { x: 0, y: 0 },
      setPosition: (position) => set({ position }),
    }),
    {
      name: 'position-storage',
      merge: (persisted, current) => deepMerge(current, persisted) as never,
    },
  ),
)

const $dotContainer = document.getElementById('dot-container') as HTMLDivElement
const $dot = document.getElementById('dot') as HTMLDivElement

$dotContainer.addEventListener('pointermove', (event) => {
  positionStore.getState().setPosition({
    x: event.clientX,
    y: event.clientY,
  })
})

const render: Parameters<typeof positionStore.subscribe>[0] = (state) => {
  console.log({ state })
  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`
}

render(positionStore.getState(), positionStore.getState())

positionStore.subscribe(render)

这是 html 代码

¥Here's the html code

html
<div
  id="dot-container"
  style="position: relative; width: 100vw; height: 100vh;"
>
  <div
    id="dot"
    style="position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;"
  ></div>
</div>

保持状态并手动补充

¥Persisting a state and hydrate it manually

在本教程中,我们将创建一个 vanilla 存储,用于跟踪由 xy 坐标表示的位置。我们还将使用 localStorage 实现持久性,并探索如何跳过补水过程并在延迟后手动触发补水。

¥In this tutorial, we’ll create a vanilla store that keeps track of a position represented by x and y coordinates. We will also implement persistence using localStorage and explore how to skip the hydration process and manually trigger rehydration after a delay.

我们首先设置一个保存位置(具有 xy 坐标的对象)的 vanilla 存储和一个更新它的操作。此外,我们还将使用 persist 中间件将位置存储在 localStorage 中,但跳过水化。

¥We start by setting up a vanilla store that holds the position (an object with x and y coordinates) and an action to update it. Furthermore, we'll also use the persist middleware to store the position in localStorage but skipping hydration.

ts
import { createStore } from 'zustand/vanilla'
import { persist } from 'zustand/middleware'

type PositionStoreState = { position: { x: number; y: number } }

type PositionStoreActions = {
  setPosition: (nextPosition: PositionStoreState['position']) => void
}

type PositionStore = PositionStoreState & PositionStoreActions

const positionStore = createStore<PositionStore>()(
  persist(
    (set) => ({
      position: { x: 0, y: 0 },
      setPosition: (position) => set({ position }),
    }),
    {
      name: 'position-storage',
      skipHydration: true,
    },
  ),
)

由于我们在初始设置中跳过了补水,我们将手动重新补水状态。在这里,我们使用 setTimeout 来模拟延迟补液。

¥Since we skipped hydration in the initial setup, we will manually rehydrate the state. Here, we’re using setTimeout to simulate a delayed rehydration.

ts
setTimeout(() => {
  positionStore.persist.rehydrate()
}, 2000)

接下来,我们将跟踪 div 内的鼠标移动并使用新位置更新存储。

¥Next, we'll track the mouse movements inside a div and update the store with the new position.

ts
const $dotContainer = document.getElementById('dot-container') as HTMLDivElement
const $dot = document.getElementById('dot') as HTMLDivElement

$dotContainer.addEventListener('pointermove', (event) => {
  positionStore.getState().setPosition({
    x: event.clientX,
    y: event.clientY,
  })
})

我们希望通过将 div 元素(代表点)移动到新坐标来反映屏幕上的位置更新。

¥We want to reflect the position updates on the screen by moving a div element (representing the dot) to the new coordinates.

ts
const render: Parameters<typeof positionStore.subscribe>[0] = (state) => {
  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`
}

render(positionStore.getState(), positionStore.getState())

positionStore.subscribe(render)

这是完整的代码。

¥Here’s the complete code.

ts
import { createStore } from 'zustand/vanilla'
import { persist } from 'zustand/middleware'

type PositionStoreState = { position: { x: number; y: number } }

type PositionStoreActions = {
  setPosition: (nextPosition: PositionStoreState['position']) => void
}

type PositionStore = PositionStoreState & PositionStoreActions

const positionStore = createStore<PositionStore>()(
  persist(
    (set) => ({
      position: { x: 0, y: 0 },
      setPosition: (position) => set({ position }),
    }),
    {
      name: 'position-storage',
      skipHydration: true,
    },
  ),
)

const $dotContainer = document.getElementById('dot-container') as HTMLDivElement
const $dot = document.getElementById('dot') as HTMLDivElement

$dotContainer.addEventListener('pointermove', (event) => {
  positionStore.getState().setPosition({
    x: event.clientX,
    y: event.clientY,
  })
})

const render: Parameters<typeof positionStore.subscribe>[0] = (state) => {
  $dot.style.transform = `translate(${state.position.x}px, ${state.position.y}px)`
}

setTimeout(() => {
  positionStore.persist.rehydrate()
}, 2000)

render(positionStore.getState(), positionStore.getState())

positionStore.subscribe(render)

这是 html 代码

¥Here's the html code

html
<div
  id="dot-container"
  style="position: relative; width: 100vw; height: 100vh;"
>
  <div
    id="dot"
    style="position: absolute; background-color: red; border-radius: 50%; left: -10px; top: -10px; width: 20px; height: 20px;"
  ></div>
</div>

故障排除

¥Troubleshooting

TBD

Zustand v5.0 中文网 - 粤ICP备13048890号