Сохранение данных хранилища¶
Промежуточное ПО Persist позволяет хранить состояние Zustand в каком-либо хранилище (например, localStorage
, AsyncStorage
, IndexedDB
и т. д.), тем самым сохраняя его данные.
Обратите внимание, что это промежуточное ПО поддерживает как синхронные хранилища, такие как localStorage
, так и асинхронные, такие как AsyncStorage
, но использование асинхронного хранилища связано с определенными затратами. Подробнее см. в разделе Гидратация и асинхронные хранилища.
Простой пример¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Параметры¶
name
¶
Это единственный обязательный параметр. Данное имя будет ключом, используемым для хранения вашего состояния Zustand в хранилище, поэтому оно должно быть уникальным.
storage
¶
Тип:
() => StateStorage
Хранилище StateStorage
может быть импортировано с помощью:
1 |
|
По-умолчанию:
createJSONStorage(() => localStorage)
Позволяет использовать собственное хранилище. Просто передайте функцию, которая возвращает хранилище, которое вы хотите использовать. Рекомендуется использовать вспомогательную функцию createJSONStorage
для создания объекта storage
, соответствующего интерфейсу StateStorage
.
Пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
partialize
¶
Тип:
(state: Object) => Object
По-умолчанию:
(state) => state
Позволяет выбрать некоторые поля состояния, которые будут хранилищем.
Вы можете опустить несколько полей, используя следующее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Или вы можете разрешить только определенные поля, используя следующее:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
onRehydrateStorage
¶
Тип:
(state: Object) => ((state?: Object, error?: Error) => void) | void
Этот параметр позволяет передать функцию-слушатель, которая будет вызываться при увлажнении хранилища.
Пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
version
¶
Тип:
number
По-умолчанию:
0
Если вы хотите внести в хранилище разрывное изменение (например, переименовать поле), вы можете указать новый номер версии. По умолчанию, если версия в хранилище не совпадает с версией в коде, хранилище не будет использоваться. Вы можете использовать функцию migrate (см. ниже) для обработки разрывных изменений, чтобы сохранить ранее хранилище данных.
migrate
¶
Тип:
(persistedState: Object, version: number) => Object | Promise<Object>
По-умолчанию:
(persistedState) => persistedState
Вы можете использовать эту опцию для обработки миграции версий. Функция migrate принимает в качестве аргументов сохраняемое состояние и номер версии. Она должна вернуть состояние, соответствующее последней версии (версия в коде).
Например, если вы хотите переименовать поле, вы можете использовать следующее:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
merge
¶
Тип:
(persistedState: Object, currentState: Object) => Object
По-умолчанию:
(persistedState, currentState) => ({ ...currentState, ...persistedState })
В некоторых случаях вы можете захотеть использовать пользовательскую функцию слияния, чтобы объединить сохраненное значение с текущим состоянием.
По умолчанию промежуточное ПО выполняет неглубокое слияние. Неглубокого слияния может быть недостаточно, если у вас есть частично сохраняемые вложенные объекты. Например, если хранилище содержит следующее:
1 2 3 4 5 |
|
Но ваше хранилище Zustand содержит:
1 2 3 4 5 6 |
|
При неглубоком слиянии поле baz
будет удалено из объекта foo
. Одним из способов исправить это было бы создание собственной функции глубокого слияния:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
skipHydration
¶
Тип:
boolean | undefined
По-умолчанию:
undefined
По умолчанию хранилище увлажняется при инициализации.
В некоторых приложениях вам может потребоваться контролировать момент первого увлажнения. Например, в приложениях с рендерингом сервера.
Если вы установите skipHydration
, начальный вызов гидратации не будет вызываться, и вам останется вручную вызвать rehydrate()
.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
API¶
Версия: >=3.6.3
Persist API позволяет вам осуществлять ряд взаимодействий с промежуточным ПО Persist изнутри или снаружи компонента React.
getOptions
¶
Тип:
() => Partial<PersistOptions>
Возвращает: Options of the Persist middleware
Например, с его помощью можно получить имя хранилища:
1 |
|
setOptions
¶
Тип:
(newOptions: Partial<PersistOptions>) => void
Изменяет параметры промежуточного ПО. Обратите внимание, что новые параметры будут объединены с текущими.
Например, это можно использовать для изменения имени хранилища:
1 2 3 |
|
Или даже сменить механизм хранения:
1 2 3 |
|
clearStorage
¶
Тип:
() => void
Очищает все, что хранилище под ключом name.
1 |
|
rehydrate
¶
Тип:
() => Promise<void>
В некоторых случаях вы можете захотеть запустить регидратацию вручную. Это можно сделать, вызвав метод rehydrate
.
1 |
|
hasHydrated
¶
Тип:
() => boolean
Это нереактивный геттер для проверки того, было ли хранилище увлажнено (обратите внимание, что он обновляется при вызове rehydrate
).
1 |
|
onHydrate
¶
Тип:
(listener: (state) => void) => () => void
Возвращает: функция отписки
Этот обработчик будет вызван, когда начнется процесс гидратации.
1 2 3 4 5 6 |
|
onFinishHydration
¶
Тип:
(listener: (state) => void) => () => void
Возвращает: функция отписки
Этот слушатель будет вызван, когда процесс гидратации завершится.
1 2 3 4 5 6 7 8 |
|
createJSONStorage
¶
Тип:
(getStorage: () => StateStorage, options?: JsonStorageOptions) => StateStorage
Возвращает:
PersistStorage
Эта вспомогательная функция позволяет создать объект storage
, что полезно, когда вы хотите использовать пользовательский механизм хранения.
getStorage
- это функция, которая возвращает механизм хранения со свойствами getItem
, setItem
и removeItem
.
options
- это необязательный объект, который может быть использован для настройки сериализации и десериализации данных. options.reviver
- это функция, которая передается в JSON.parse
для десериализации данных. options.replacer
- это функция, которая передается в JSON.stringify
для сериализации данных.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
Гидратация и асинхронные хранилища¶
Чтобы объяснить, в чем заключается "стоимость" асинхронных хранилищ, необходимо понять, что такое гидратация.
В двух словах, гидратация - это процесс извлечения сохраненного состояния из хранилища и слияния его с текущим состоянием.
Промежуточное ПО Persist выполняет два вида гидратации: синхронную и асинхронную. Если данное хранилище является синхронным (например, localStorage
), гидратация будет выполняться синхронно. С другой стороны, если данное хранилище является асинхронным (например, AsyncStorage
), гидратация будет выполняться асинхронно (шокирующе, я знаю!).
Но в чем подвох? При синхронном гидратировании хранилище Zustand уже будет гидратировано при его создании. При асинхронном увлажнении хранилище Zustand, напротив, будет увлажнено позже, в микрозадаче.
Почему это важно? Асинхронная гидратация может привести к неожиданному поведению. Например, если вы используете Zustand в приложении React, хранилище не будет гидратировано при начальном рендере. В случаях, когда ваше приложение зависит от сохраняемого значения при загрузке страницы, вы можете захотеть подождать, пока хранилище будет гидратировано, прежде чем показывать что-либо. Например, ваше приложение может думать, что пользователь не вошел в систему, потому что так принято по умолчанию, но на самом деле хранилище еще не гидратировано.
Если ваше приложение зависит от сохраненного состояния при загрузке страницы, смотрите Как я могу проверить, было ли мое хранилище гидратировано в разделе FAQ ниже.
Использование в Next.js¶
NextJS использует Server Side Rendering, и он будет сравнивать рендеринг компонента на сервере с рендерингом на клиенте. Но поскольку вы используете данные из браузера для изменения компонента, два рендера будут отличаться, и Next выдаст вам предупреждение.
Обычно ошибки выглядят следующим образом:
- Текстовое содержимое не соответствует HTML, отрендеренному на сервере
- Гидратация не удалась, потому что исходный пользовательский интерфейс не соответствует тому, что было отрисовано на сервере
- Во время гидрирования произошла ошибка. Поскольку ошибка произошла за пределами границы Suspense, весь корень переключится на клиентский рендеринг.
Чтобы устранить эти ошибки, создайте пользовательский хук, чтобы Zustand немного подождал перед изменением компонентов.
Создайте файл со следующими параметрами:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Теперь на своих страницах вы будете использовать хук немного по-другому:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
1 2 3 4 5 6 7 8 9 |
|
Кредиты: Этот ответ на вопрос, который указывает на эту запись в блоге.
FAQ¶
Как я могу проверить, было ли мое хранилище увлажнено¶
Есть несколько различных способов сделать это.
Вы можете использовать функцию-слушатель onRehydrateStorage
для обновления поля в хранилище:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Вы также можете создать собственный хук useHydration
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Как я могу использовать пользовательский механизм хранения данных¶
Если хранилище, которое вы хотите использовать, не соответствует ожидаемому API, вы можете создать собственное хранилище:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
|
Если вы используете тип, который JSON.stringify()
не поддерживает, вам придется написать свой собственный код сериализации/десериализации. Однако, если это утомительно, вы можете использовать сторонние библиотеки для сериализации и десериализации различных типов данных.
Например, Superjson может сериализовать данные вместе с их типом, позволяя при десериализации вернуть их к исходному типу
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
|
Как я могу регидратировать по событию хранения¶
Вы можете использовать Persist API для создания собственной реализации, подобной приведенному ниже примеру:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Как использовать его с TypeScript¶
Базовое использование в TypeScript не требует ничего особенного, кроме написания create<State>()(...)
вместо create(...)
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
Как использовать его с Map и Set¶
Чтобы сохранить такие типы объектов, как Map
и Set
, их необходимо преобразовать в JSON-сериализуемые типы, такие как Array
, что можно сделать, определив пользовательский движок storage
.
Допустим, ваше состояние использует Map
для обработки списка transactions
, тогда вы можете преобразовать Map
в Array
в свойстве storage
, которое показано ниже:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Источник — https://docs.pmnd.rs/zustand/integrations/persisting-store-data