@anfenn/zync 0.1.21 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +57 -31
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -27,13 +27,14 @@ Unopinionated, bullet-proof, offline-first sync middleware for Zustand.
27
27
  npm install @anfenn/zync
28
28
  ```
29
29
 
30
- ### Zustand store creation:
30
+ ### Zustand store creation (store.ts):
31
31
 
32
32
  ```ts
33
- import { SyncAction, UseStoreWithSync, persistWithSync } from '@anfenn/zync';
33
+ import { SyncAction, type UseStoreWithSync, persistWithSync } from '@anfenn/zync';
34
34
  import { create } from 'zustand';
35
35
  import { createJSONStorage } from 'zustand/middleware';
36
36
  import { useShallow } from 'zustand/react/shallow';
37
+ import { factApi, type Fact } from './api';
37
38
 
38
39
  type Store = {
39
40
  facts: Fact[];
@@ -57,14 +58,14 @@ export const useStore = create<any>()(
57
58
  }));
58
59
 
59
60
  // Never call queueToSync() inside Zustand set() due to itself calling set(), so may cause lost state changes
60
- queueToSync(SyncAction.CreateOrUpdate, item._localId, 'facts');
61
+ queueToSync(SyncAction.CreateOrUpdate, 'facts', item._localId);
61
62
  },
62
63
  updateFact: (localId: string, changes: Partial<Fact>) => {
63
64
  set((state: Store) => ({
64
65
  facts: state.facts.map((item) => (item._localId === localId ? { ...item, ...changes } : item)),
65
66
  }));
66
67
 
67
- queueToSync(SyncAction.CreateOrUpdate, localId, 'facts');
68
+ queueToSync(SyncAction.CreateOrUpdate, 'facts', localId);
68
69
  },
69
70
  removeFact: (localId: string) => {
70
71
  queueToSync(SyncAction.Remove, 'facts', localId);
@@ -79,7 +80,7 @@ export const useStore = create<any>()(
79
80
 
80
81
  name: 'store',
81
82
  storage: createJSONStorage(() => localStorage),
82
- // storage: createJSONStorage(() => createIndexedDBStorage({ dbName: 'my-app', storeName: 'store' })),
83
+ // OR storage: createJSONStorage(() => createIndexedDBStorage({ dbName: 'my-app', storeName: 'store' })),
83
84
  },
84
85
  {
85
86
  // State-to-API map to enable syncing. Must implement the full CRUD API:
@@ -109,30 +110,59 @@ export const useFacts = () =>
109
110
  ### In your component:
110
111
 
111
112
  ```ts
112
- // Your state
113
- const { facts, addFact } = useFacts();
114
-
115
- // Zync's internal sync state
116
- const syncState = useStore((state) => state.syncState);
117
- // syncState.status // 'hydrating' | 'syncing' | 'idle'
118
- // syncState.error
119
- // syncState.enabled
120
- // syncState.firstLoadDone
121
- // syncState.pendingChanges
122
- // syncState.lastPulled
123
-
124
- // Zync's control api
125
- useStore.sync.enable(true | false); // Defaults to false, so turn on to start syncing
126
- useStore.sync.startFirstLoad();
113
+ import { useEffect } from 'react';
114
+ import { nextLocalId } from '@anfenn/zync';
115
+ import { useFacts, useStore } from './store';
116
+
117
+ function App() {
118
+ // Your state
119
+ const { facts, addFact } = useFacts();
120
+
121
+ // Zync's internal sync state
122
+ const syncState = useStore((state) => state.syncState);
123
+ // syncState.status // 'hydrating' | 'syncing' | 'idle'
124
+ // syncState.error
125
+ // syncState.enabled
126
+ // syncState.firstLoadDone
127
+ // syncState.pendingChanges
128
+ // syncState.lastPulled
129
+
130
+ useEffect(() => {
131
+ // Zync's control api
132
+ useStore.sync.enable(true); // Defaults to false, enable to start syncing
133
+ //useStore.sync.startFirstLoad(); // Batch loads from server
134
+ }, []);
135
+
136
+ return (
137
+ <>
138
+ <div>Sync Status: {syncState.status}</div>
139
+ <button
140
+ onClick={() =>
141
+ addFact({
142
+ _localId: nextLocalId(),
143
+ title: 'New fact ' + Date.now(),
144
+ })
145
+ }
146
+ >
147
+ Add Fact
148
+ </button>
149
+ {
150
+ facts.map((fact) => (
151
+ <div key={fact._localId}>{fact.title}</div>
152
+ ))
153
+ }
154
+ </>
155
+ );
156
+ }
127
157
  ```
128
158
 
129
- ### In your API:
159
+ ### In your api.ts:
130
160
 
131
161
  _(Supabase example, but could be fetch, GraphQL, etc.)_
132
162
 
133
163
  ```ts
134
- import { ApiFunctions } from '@anfenn/zync';
135
- import { supabase } from './supabase';
164
+ import type { ApiFunctions } from '@anfenn/zync';
165
+ import { supabase } from './supabase'; // Please include your own :)
136
166
 
137
167
  export type Fact = {
138
168
  _localId: string;
@@ -212,23 +242,19 @@ async function firstLoad(lastId: any) {
212
242
 
213
243
  ## Optional IndexedDB storage
214
244
 
215
- When using IndexedDB Zustand saves the whole store under one key, which means indexes cannot be used to accelerate querying. However, if this becomes a performance issue due to the size of the store, then libraries like dexie.js instead of Zustand would be a better solution and provide the syntax for high performance queries.
245
+ Using async IndexedDB over sync localStorage gives the advantage of a responsive UI when reading/writing a very large store, as IndexedDB is running in it's own thread.
216
246
 
217
247
  If you want to use the bundled `createIndexedDBStorage()` helper, install `idb` in your project. It's intentionally optional so projects that don't use IndexedDB won't pull the dependency into their bundles.
218
248
 
219
- Install for runtime usage:
249
+ [idb](https://www.npmjs.com/package/idb) is an extremely popular and lightweight wrapper to simplify IndexedDB's verbose events based api into a simple Promise based one. It also handles the inconsistencies found when running in different browsers.
220
250
 
221
251
  ```bash
222
252
  npm install idb
223
253
  ```
224
254
 
225
- Or add it as an optional dependency of your package:
226
-
227
- ```bash
228
- npm install --save-optional idb
229
- ```
255
+ When using IndexedDB Zustand saves the whole store under one key, which means indexes cannot be used to accelerate querying. However, if this becomes a performance issue due to the size of the store, then libraries like dexie.js instead of Zustand would be a better solution and provide the syntax for high performance queries.
230
256
 
231
- The library will throw a helpful runtime error if `idb` isn't installed when `createIndexedDBStorage()` is invoked.
257
+ From testing I've found Zustand and Zync are lightening fast even with 100,000 average sized state objects.
232
258
 
233
259
  ## Community
234
260
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anfenn/zync",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "private": false,
5
5
  "description": "Sync middleware for Zustand",
6
6
  "keywords": [],