主题
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 takesset
function,get
function andstore
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 tocreateJSONStorage(() => 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 tofalse
. Iftrue
, the middleware won't automatically rehydrate the state on initialization. Userehydrate
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.
我们首先设置一个保存位置(具有 x
和 y
坐标的对象)的 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 搜索参数用作存储,我们定义了一个具有 getItem
、setItem
和 removeItem
方法的 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 搜索参数中保留位置数据,而不是默认的 localStorage
或 sessionStorage
。
¥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 状态将坐标保存为 x
和 y
字段。
¥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(其中 x
和 y
是分开的) migrate
到 version
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 存储,用于跟踪由 x
和 y
坐标表示的位置。我们还将使用 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 存储,用于跟踪由 x
和 y
坐标表示的位置。我们还将使用 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.
我们首先设置一个保存位置(具有 x
和 y
坐标的对象)的 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