{"id":13756519,"url":"https://github.com/yumauri/effector-storage","last_synced_at":"2025-05-16T14:05:00.653Z","repository":{"id":36137377,"uuid":"221760348","full_name":"yumauri/effector-storage","owner":"yumauri","description":"Module for Effector ☄️ to sync stores with `localStorage` (or `sessionStorage`)","archived":false,"fork":false,"pushed_at":"2025-05-15T20:02:44.000Z","size":2393,"stargazers_count":108,"open_issues_count":24,"forks_count":3,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-05-15T21:23:23.714Z","etag":null,"topics":["effector","effector-storage","localstorage","sessionstorage"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yumauri.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-11-14T18:18:54.000Z","updated_at":"2025-04-02T16:17:45.000Z","dependencies_parsed_at":"2023-09-21T22:57:16.015Z","dependency_job_id":"750e30ad-1750-49ba-a99e-55ec14748666","html_url":"https://github.com/yumauri/effector-storage","commit_stats":{"total_commits":207,"total_committers":4,"mean_commits":51.75,"dds":0.01449275362318836,"last_synced_commit":"76361493b2978cd70394c017354ebd9ae6f6cad6"},"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yumauri%2Feffector-storage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yumauri%2Feffector-storage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yumauri%2Feffector-storage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yumauri%2Feffector-storage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yumauri","download_url":"https://codeload.github.com/yumauri/effector-storage/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254423209,"owners_count":22068754,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["effector","effector-storage","localstorage","sessionstorage"],"created_at":"2024-08-03T11:00:46.051Z","updated_at":"2025-05-16T14:05:00.622Z","avatar_url":"https://github.com/yumauri.png","language":"TypeScript","funding_links":[],"categories":["Packages"],"sub_categories":[],"readme":"# effector-storage\n\n[![Build Status](https://github.com/yumauri/effector-storage/workflows/build/badge.svg)](https://github.com/yumauri/effector-storage/actions?workflow=build)\n[![License](https://img.shields.io/github/license/yumauri/effector-storage.svg?color=yellow)](./LICENSE)\n[![NPM](https://img.shields.io/npm/v/effector-storage.svg)](https://www.npmjs.com/package/effector-storage)\n![Made with Love](https://img.shields.io/badge/made%20with-❤-red.svg)\n\nSmall module for [Effector](https://github.com/effector/effector) ☄️ to sync stores with different storages (local storage, session storage, async storage, IndexedDB, cookies, server side storage, etc).\n\n## Table of Contents\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n- [Install](#install)\n- [Usage](#usage)\n  - [with `localStorage`](#with-localstorage)\n  - [with `sessionStorage`](#with-sessionstorage)\n  - [with query string](#with-query-string)\n  - [with `BroadcastChannel`](#with-broadcastchannel)\n  - [extra adapters](#extra-adapters)\n- [Usage with domains](#usage-with-domains)\n- [Formulae](#formulae)\n  - [Units](#units)\n  - [Options](#options)\n  - [Returns](#returns)\n  - [Contracts](#contracts)\n  - [Notes](#notes)\n- [`createPersist` factory](#createpersist-factory)\n  - [Options](#options-1)\n  - [Returns](#returns-1)\n- [Advanced usage](#advanced-usage)\n- [Storage adapters](#storage-adapters)\n  - [Synchronous storage adapter example](#synchronous-storage-adapter-example)\n  - [Asynchronous storage adapter example](#asynchronous-storage-adapter-example)\n  - [Storage with external updates example](#storage-with-external-updates-example)\n  - [Update from non-reactive storage](#update-from-non-reactive-storage)\n  - [Local storage adapter with values expiration](#local-storage-adapter-with-values-expiration)\n  - [Custom `Storage` adapter](#custom-storage-adapter)\n- [FAQ](#faq)\n  - [Can I persist part of the store?](#can-i-persist-part-of-the-store)\n- [TODO](#todo)\n- [Sponsored](#sponsored)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Install\n\nDepending on your package manager\n\n```bash\n# using `pnpm` ↓\n$ pnpm add effector-storage\n\n# using `yarn` ↓\n$ yarn add effector-storage\n\n# using `npm` ↓\n$ npm install --save effector-storage\n```\n\n## Usage\n\n### with `localStorage`\n\nDocs: [effector-storage/local](https://github.com/yumauri/effector-storage/tree/main/src/local/README.md)\n\n```javascript\nimport { persist } from 'effector-storage/local'\n\n// persist store `$counter` in `localStorage` with key 'counter'\npersist({ store: $counter, key: 'counter' })\n\n// if your storage has a name, you can omit `key` field\npersist({ store: $counter })\n```\n\nStores, persisted in `localStorage`, are automatically synced between two (or more) windows/tabs. Also, they are synced between instances, so if you will persist two stores with the same key — each store will receive updates from another one.\n\nℹ️ If you need just basic bare minimum functionality, you can take a look at [`effector-localstorage`](https://github.com/ilyalesik/effector-localstorage) library. It has similar API, it much simpler and tinier.\n\n### with `sessionStorage`\n\nDocs: [effector-storage/session](https://github.com/yumauri/effector-storage/tree/main/src/session/README.md)\n\nSame as above, just import `persist` from `'effector-storage/session'`:\n\n```javascript\nimport { persist } from 'effector-storage/session'\n```\n\nStores, persisted in `sessionStorage`, are synced between instances, but not between different windows/tabs.\n\n### with query string\n\nDocs: [effector-storage/query](https://github.com/yumauri/effector-storage/tree/main/src/query/README.md)\n\nYou can _reflect_ plain string store value in query string parameter, using this adapter. Think of it like about synchronizing store value and query string parameter.\n\n```javascript\nimport { persist } from 'effector-storage/query'\n\n// persist store `$id` in query string parameter 'id'\npersist({ store: $id, key: 'id' })\n```\n\nIf two (or more) stores are persisted in query string with the same key — they are synced between themselves.\n\n### with `BroadcastChannel`\n\nDocs: [effector-storage/broadcast](https://github.com/yumauri/effector-storage/tree/main/src/broadcast/README.md)\n\nYou can sync stores across different browsing contexts (tabs, windows, workers), just import `persist` from `'effector-storage/broadcast'`:\n\n```javascript\nimport { persist } from 'effector-storage/broadcast'\n```\n\n### extra adapters\n\nYou can find a collection of useful adapters in [effector-storage-extras](https://github.com/yumauri/effector-storage-extras). That side repository was created in order to not bloat `effector-storage` with dependencies and adapters, which depends on other libraries.\n\n## Usage with domains\n\nYou can use `persist` inside Domain's `onCreateStore` hook:\n\n```javascript\nimport { createDomain } from 'effector'\nimport { persist } from 'effector-storage/local'\n\nconst app = createDomain('app')\n\n// this hook will persist every store, created in domain,\n// in `localStorage`, using stores' names as keys\napp.onCreateStore((store) =\u003e persist({ store }))\n\nconst $store = app.createStore(0, { name: 'store' })\n```\n\n## Formulae\n\n```javascript\nimport { persist } from 'effector-storage/\u003cadapter\u003e'\n```\n\n- `persist({ store, ...options }): Subscription`\n- `persist({ source, target, ...options }): Subscription`\n\n### Units\n\nIn order to synchronize _something_, you need to specify effector units. Depending on a requirements, you may want to use `store` parameter, or `source` and `target` parameters:\n\n- `store` ([_Store_]): Store to synchronize with local/session storage.\n- `source` ([_Event_] | [_Effect_] | [_Store_]): Source unit, which updates will be sent to local/session storage.\n- `target` ([_Event_] | [_Effect_] | [_Store_]): Target unit, which will receive updates from local/session storage (as well as initial value). Must be different than `source` to avoid circular updates — `source` updates are passed directly to `target`.\n\n### Options\n\n- `key`? ([_string_]): Key for local/session storage, to store value in. If omitted — `store` name is used. **Note!** If `key` is not specified, `store` _must_ have a `name`! You can use `'effector/babel-plugin'` to have those names automatically.\n- `keyPrefix`? ([_string_]): Prefix, used in adapter, to be concatenated to `key`. By default = `''`.\n- `clock`? ([_Event_] | [_Effect_] | [_Store_]): Unit, if passed – then value from `store`/`source` will be stored in the storage only upon its trigger.\n- `pickup`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which you can specify to update `store` value from storage. This unit can also set a special context for adapter. **Note!** When you add `pickup`, `persist` _will not_ get initial value from storage automatically!\n- `context`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which can set a special context for adapter.\n- `contract`? ([_Contract_]): Rule to statically validate data from storage.\n- `done`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which will be triggered on each successful read or write from/to storage.\u003cbr\u003e\n  Payload structure:\n  - `key` ([_string_]): Same `key` as above.\n  - `keyPrefix` ([_string_]): Prefix, used in adapter, to be concatenated to `key`. By default = `''`.\n  - `operation` (_`'set'`_ | _`'get'`_): Type of operation, read (get) or write (set).\n  - `value` (_State_): Value set to `store` or got from `store`.\n- `fail`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which will be triggered in case of any error (serialization/deserialization error, storage is full and so on). **Note!** If `fail` unit is not specified, any errors will be printed using `console.error(Error)`.\u003cbr\u003e\n  Payload structure:\n  - `key` ([_string_]): Same `key` as above.\n  - `keyPrefix` ([_string_]): Prefix, used in adapter, to be concatenated to `key`. By default = `''`.\n  - `operation` (_`'set'`_ | _`'get'`_ | _`'validate'`_): Type of operation, read (get), write (set) or validation against contract (validate).\n  - `error` ([_Error_]): Error instance\n  - `value`? (_any_): In case of _'set'_ operation — value from `store`. In case of _'get'_ operation could contain raw value from storage or could be empty.\n- `finally`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which will be triggered either in case of success or error.\u003cbr\u003e\n  Payload structure:\n  - `key` ([_string_]): Same `key` as above.\n  - `keyPrefix` ([_string_]): Prefix, used in adapter, to be concatenated to `key`. By default = `''`.\n  - `operation` (_`'set'`_ | _`'get'`_ | _`'validate'`_): Type of operation, read (get), write (set) or validation against contract (validate).\n  - `status` (_`'done'`_ | _`'fail'`_): Operation status.\n  - `error`? ([_Error_]): Error instance, in case of error.\n  - `value`? (_any_): Value, in case it is exists (look above).\n\n### Returns\n\n- ([_Subscription_]): You can use this subscription to remove store association with storage, if you don't need them to be synced anymore. It is a function.\n\n### Contracts\n\nYou can use `contract` option to validate data from storage. Contract has the following type definition:\n\n```typescript\nexport type Contract\u003cData\u003e =\n  | ((raw: unknown) =\u003e raw is Data)\n  | {\n      isData: (raw: unknown) =\u003e raw is Data\n      getErrorMessages: (raw: unknown) =\u003e string[]\n    }\n```\n\nSo, it could be simple type guard function in trivial use cases, or more complex object with `isData` type guard and `getErrorMessages` function, which returns array of error messages. This format is fully compatible with [Farfetched contracts](https://withease.effector.dev/protocols/contract.html), so you can use any adapter from Farfetched ([runtypes](https://ff.effector.dev/api/contracts/runtypes.html), [zod](https://ff.effector.dev/api/contracts/zod.html), [io-ts](https://ff.effector.dev/api/contracts/io-ts.html), [superstruct](https://ff.effector.dev/api/contracts/superstruct.html), [typed-contracts](https://ff.effector.dev/api/contracts/typed-contracts.html), [valibot](https://ff.effector.dev/api/contracts/valibot.html)) with `persist` and `contract` option:\n\n```typescript\n// simple type guard\npersist({\n  store: $counter,\n  key: 'counter',\n  contract: (raw): raw is number =\u003e typeof raw === 'number',\n})\n```\n\n```typescript\n// complex contract with Farfetched adapter\nimport * as s from 'superstruct'\nimport { superstructContract } from '@farfetched/superstruct'\n\nconst Asteroid = s.type({\n  type: s.literal('asteroid'),\n  mass: s.number(),\n})\n\npersist({\n  store: $asteroid,\n  key: 'asteroid',\n  contract: superstructContract(Asteroid),\n})\n```\n\nThere are two gotchas with contracts:\n\n1. From `effector-storage` point of view it is absolutely normal, when there is no persisted value in the storage yet. So, `undefined` value is _always valid_, even if contract does not explicitly allow it.\n2. `effector-storage` does not prevent persisting invalid data to the storage, but it will validate it nonetheless, after persisting, so, if you write invalid data to the storage, `fail` will be triggered, but data will be persisted.\n\n### Notes\n\nWithout specifying `pickup` property, calling `persist` will immediately call adapter to get initial value. In case of synchronous storage (like `localStorage` or `sessionStorage`) this action will synchronously set store value, and call `done`/`fail`/`finally` right away. You should take that into account, if you adds some logic on `done`, for example — place `persist` after that logic (see issue [#38](https://github.com/yumauri/effector-storage/issues/38) for more details).\n\nYou can modify adapter to be asynchronous to mitigate this behavior with [`async`](https://github.com/yumauri/effector-storage/tree/main/src/tools/README.md#async) function.\n\n## `createPersist` factory\n\nIn rare cases you might want to use `createPersist` factory. It allows you to specify some adapter options, like `keyPrefix`.\n\n```javascript\nimport { createPersist } from 'effector-storage/local'\n\nconst persist = createPersist({\n  keyPrefix: 'app/',\n})\n\n// ---8\u003c---\n\npersist({\n  store: $store1,\n  key: 'store1', // localStorage key will be `app/store1`\n})\npersist({\n  store: $store2,\n  key: 'store2', // localStorage key will be `app/store2`\n})\n```\n\n### Options\n\n- `pickup`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which you can specify to update `store` value from storage. This unit can also set a special context for adapter. **Note!** When you add `pickup`, `persist` _will not_ get initial value from storage automatically!\n- `context`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which can set a special context for adapter.\n- `keyPrefix`? ([_string_]): Key prefix for adapter. It will be concatenated with any `key`, given to returned `persist` function.\n- `contract`? ([_Contract_]): Rule to statically validate data from storage.\n\n### Returns\n\n- Custom `persist` function, with predefined adapter options.\n\n## Advanced usage\n\n`effector-storage` consists of a _core_ module and _adapter_ modules.\n\nThe core module itself does nothing with actual storage, it just connects effector units to the storage adapter, using couple of _Effects_ and bunch of connections.\n\nThe storage adapter _gets_ and _sets_ values, and also can asynchronously emit values on storage updates.\n\n```javascript\nimport { persist } from 'effector-storage'\n```\n\nCore function `persist` accepts all **common** options, as `persist` functions from sub-modules, plus additional one:\n\n- `adapter` (_StorageAdapter_): Storage adapter to use.\n\n## Storage adapters\n\nAdapter is a function, which is called by the core `persist` function, and has following interface:\n\n```typescript\ninterface StorageAdapter {\n  \u003cState\u003e(\n    key: string,\n    update: (raw?: any) =\u003e void\n  ): {\n    get(raw?: any, ctx?: any): State | undefined | Promise\u003cState | undefined\u003e\n    set(value: State, ctx?: any): void | Promise\u003cvoid\u003e\n  }\n  keyArea?: any\n  noop?: boolean\n}\n```\n\n#### Arguments\n\n- `key` ([_string_]): Unique key to distinguish values in storage.\n- `update` ([_Function_]): Function, which could be called to get value from storage. In fact this is `Effect` with `get` function as a handler. In other words, any argument, passed to `update` function, will end up as argument in `get` function.\n\n#### Returns\n\n- `{ get, set }` (_{ Function, Function }_): Getter from and setter to storage. These functions are used as Effects handlers, and could be sync or async. Also, you don't have to catch exceptions and errors inside those functions — Effects will do that for you.\u003cbr\u003e\n  As mentioned above, call of `update` function will trigger `get` function with the same argument. So you can handle cases, when `get` function is called during initial `persist` execution (without arguments), or after external update. Check out [example below](#storage-with-external-updates-example).\u003cbr\u003e\n  Also getter and setter both accepts optional _context_ as a second argument — it can be any value. This context could be useful, if adapter depends on some external environment, for example, it can contain _Request_ and _Response_ from Express middleware, to get/set cookies from/to. (TODO: isomorphic cookies adapter example).\n\n#### keyArea\n\nAdapter function can have static field `keyArea` — this could be any value of any type, which should be unique for _keys namespace_. For example, two local storage adapters could have different settings, but both of them uses same _storage area_ — `localStorage`. So, different stores, persisted in local storage with the same key (but possibly with different adapters), should be synced. That is what `keyArea` is responsible for. Value of that field is used as a key in cache `Map`.\u003cbr\u003e\nIn case it is omitted — adapter instances is used instead.\n\n#### noop\n\nMarks adapter as \"no-op\" for [`either`](https://github.com/yumauri/effector-storage/tree/main/src/tools/README.md#either) function.\n\n### Synchronous storage adapter example\n\nFor example, simplified _localStorage_ adapter might looks like this. This is over-simplified example, don't do that in real code, there are no serialization and deserialization, no checks for edge cases. This is just to show an idea.\n\n```javascript\nimport { createStore } from 'effector'\nimport { persist } from 'effector-storage'\n\nconst adapter = (key) =\u003e ({\n  get: () =\u003e localStorage.getItem(key),\n  set: (value) =\u003e localStorage.setItem(key, value),\n})\n\nconst store = createStore('', { name: 'store' })\npersist({ store, adapter }) // \u003c- use adapter\n```\n\n### Asynchronous storage adapter example\n\nUsing asynchronous storage is just as simple. Once again, this is just a bare simple idea, without serialization and edge cases checks. If you need to use React Native Async Storage, try [@effector-storage/react-native-async-storage](https://github.com/yumauri/effector-storage-extras/tree/main/packages/react-native-async-storage)) adapter instead.\n\n```javascript\nimport AsyncStorage from '@react-native-async-storage/async-storage'\nimport { createStore } from 'effector'\nimport { persist } from 'effector-storage'\n\nconst adapter = (key) =\u003e ({\n  get: async () =\u003e AsyncStorage.getItem(key),\n  set: async (value) =\u003e AsyncStorage.setItem(key, value),\n})\n\nconst store = createStore('', { name: '@store' })\npersist({ store, adapter }) // \u003c- use adapter\n```\n\n### Storage with external updates example\n\nIf your storage can be updated from an _external source_, then adapter needs a way to inform/update connected store. That is where you will need second `update` argument.\n\n```javascript\nimport { createStore } from 'effector'\nimport { persist } from 'effector-storage'\n\nconst adapter = (key, update) =\u003e {\n  addEventListener('storage', (event) =\u003e {\n    if (event.key === key) {\n      // kick update\n      // this will call `get` function from below ↓\n      // wrapped in Effect, to handle any errors\n      update(event.newValue)\n    }\n  })\n\n  return {\n    // `get` function will receive `newValue` argument\n    // from `update`, called above ↑\n    get: (newValue) =\u003e newValue || localStorage.getItem(key),\n    set: (value) =\u003e localStorage.setItem(key, value),\n  }\n}\n\nconst store = createStore('', { name: 'store' })\npersist({ store, adapter }) // \u003c- use adapter\n```\n\n### Update from non-reactive storage\n\nIf your storage can be updated from _external source_, and doesn't have any events to react to, but you are able to know about it somehow.\n\nYou can use optional `pickup` parameter to specify unit to trigger update (keep in mind, that when you add `pickup`, `persist` _will not_ get initial value from storage automatically):\n\n```javascript\nimport { createEvent, createStore } from 'effector'\nimport { persist } from 'effector-storage/session'\n\n// event, which will be used to trigger update\nconst pickup = createEvent()\n\nconst store = createStore('', { name: 'store' })\npersist({ store, pickup }) // \u003c- set `pickup` parameter\n\n// --8\u003c--\n\n// when you are sure, that storage was updated,\n// and you need to update `store` from storage with new value\npickup()\n```\n\nAnother option, if you have your own adapter, you can add this feature right into it:\n\n```javascript\nimport { createEvent, createStore } from 'effector'\nimport { persist } from 'effector-storage'\n\n// event, which will be used in adapter to react to\nconst pickup = createEvent()\n\nconst adapter = (key, update) =\u003e {\n  // if `pickup` event was triggered -\u003e call an `update` function\n  // this will call `get` function from below ↓\n  // wrapped in Effect, to handle any errors\n  pickup.watch(update)\n  return {\n    get: () =\u003e localStorage.getItem(key),\n    set: (value) =\u003e localStorage.setItem(key, value),\n  }\n}\n\nconst store = createStore('', { name: 'store' })\npersist({ store, adapter }) // \u003c- use your adapter\n\n// --8\u003c--\n\n// when you are sure, that storage was updated,\n// and you need to force update `store` from storage with new value\npickup()\n```\n\n### Local storage adapter with values expiration\n\n\u003e I want sync my store with `localStorage`, but I need smart synchronization, not dumb. Each storage update should contain last write timestamp. And on read value I need to check if value has been expired, and fill store with default value in that case.\n\nYou can implement it with custom adapter, something like this:\n\n```javascript\nimport { createStore } from 'effector'\nimport { persist } from 'effector-storage'\n\nconst adapter = (timeout) =\u003e (key) =\u003e ({\n  get() {\n    const item = localStorage.getItem(key)\n    if (item === null) return // no value in localStorage\n    const { time, value } = JSON.parse(item)\n    if (time + timeout * 1000 \u003c Date.now()) return // value has expired\n    return value\n  },\n\n  set(value) {\n    localStorage.setItem(key, JSON.stringify({ time: Date.now(), value }))\n  },\n})\n\nconst store = createStore('', { name: 'store' })\n\n// use adapter with timeout = 1 hour ↓↓↓\npersist({ store, adapter: adapter(3600) })\n```\n\n### Custom `Storage` adapter\n\nBoth `'effector-storage/local'` and `'effector-storage/session'` are using common `storage` adapter factory. If you want to use _other storage_, which implements [`Storage`](https://developer.mozilla.org/en-US/docs/Web/API/Storage) interface (in fact, synchronous `getItem` and `setItem` methods are enough) — you can use this factory.\n\n```javascript\nimport { storage } from 'effector-storage/storage'\n```\n\n```javascript\nadapter = storage(options)\n```\n\n#### Options\n\n- `storage` (_Storage_): Storage to communicate with.\n- `sync`? ([_boolean_] | 'force'): Add [`'storage'`] event listener or no. Default = `false`. In case of `'force'` value adapter will always read new value from _Storage_, instead of event.\n- `serialize`? (_(value: any) =\u003e string_): Custom serialize function. Default = `JSON.stringify`\n- `deserialize`? (_(value: string) =\u003e any_): Custom deserialize function. Default = `JSON.parse`\n\n#### Returns\n\n- (StorageAdapter): Storage adapter, which can be used with the core `persist` function.\n\n## FAQ\n\n### Can I persist part of the store?\n\nThe issue here is that it is hardly possible to create universal mapping to/from storage to the part of the store within the library implementation. But with `persist` form with `source`/`target`, and little help of Effector API you can make it:\n\n```javascript\nimport { persist } from 'effector-storage/local'\n\nconst setX = createEvent()\nconst setY = createEvent()\nconst $coords = createStore({ x: 123, y: 321 })\n  .on(setX, ({ y }, x) =\u003e ({ x, y }))\n  .on(setY, ({ x }, y) =\u003e ({ x, y }))\n\n// persist X coordinate in `localStorage` with key 'x'\npersist({\n  source: $coords.map(({ x }) =\u003e x),\n  target: setX,\n  key: 'x',\n})\n\n// persist Y coordinate in `localStorage` with key 'y'\npersist({\n  source: $coords.map(({ y }) =\u003e y),\n  target: setY,\n  key: 'y',\n})\n```\n\n⚠️ **BIG WARNING!**\u003cbr\u003e\nUse this approach with caution, beware of infinite circular updates. To avoid them, persist _only plain values_ in storage. So, mapped store in `source` will not trigger update, if object in original store has changed. Also, you can take a look at [`updateFilter` option](https://effector.dev/docs/api/effector/createStore).\n\n## TODO\n\n- [x] [localStorage] support (docs: [effector-storage/local](https://github.com/yumauri/effector-storage/tree/main/src/local/README.md))\n- [x] [sessionStorage] support (docs: [effector-storage/session](https://github.com/yumauri/effector-storage/tree/main/src/session/README.md))\n- [x] [query string](https://developer.mozilla.org/en-US/docs/Web/API/Location/search) support (docs: [effector-storage/query](https://github.com/yumauri/effector-storage/tree/main/src/query/README.md))\n- [x] [BroadcastChannel] support (docs: [effector-storage/broadcast](https://github.com/yumauri/effector-storage/tree/main/src/broadcast/README.md))\n- [x] [AsyncStorage] support (extras: [@effector-storage/react-native-async-storage](https://github.com/yumauri/effector-storage-extras/tree/main/packages/react-native-async-storage))\n- [x] [EncryptedStorage] support (extras: [@effector-storage/react-native-encrypted-storage](https://github.com/yumauri/effector-storage-extras/tree/main/packages/react-native-encrypted-storage))\n- [x] [IndexedDB] support (extras: [@effector-storage/idb-keyval](https://github.com/yumauri/effector-storage-extras/tree/main/packages/idb-keyval))\n- [ ] [Cookies] support\n- [ ] you name it support\n\n## Sponsored\n\n[\u003cimg src=\"https://setplex.com/img/logo.png\" alt=\"Setplex OTT Platform\" width=\"236\"\u003e](https://setplex.com/en/)\n\n[Setplex OTT Platform](https://setplex.com/en/)\n\n[localstorage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage\n[sessionstorage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage\n[`'storage'`]: https://developer.mozilla.org/en-US/docs/Web/API/StorageEvent\n[indexeddb]: https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API\n[broadcastchannel]: https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel\n[asyncstorage]: https://react-native-async-storage.github.io/async-storage/\n[encryptedstorage]: https://github.com/emeraldsanto/react-native-encrypted-storage\n[cookies]: https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie\n[_subscription_]: https://effector.dev/docs/glossary#subscription\n[_effect_]: https://effector.dev/docs/api/effector/effect\n[_event_]: https://effector.dev/docs/api/effector/event\n[_store_]: https://effector.dev/docs/api/effector/store\n[_string_]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String\n[_function_]: https://developer.mozilla.org/en-US/docs/Glossary/Function\n[_boolean_]: https://developer.mozilla.org/en-US/docs/Glossary/Boolean\n[_error_]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error\n[_Contract_]: #contracts\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyumauri%2Feffector-storage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyumauri%2Feffector-storage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyumauri%2Feffector-storage/lists"}