@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.
- package/README.md +57 -31
- 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
|
|
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,
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
// syncState.
|
|
123
|
-
|
|
124
|
-
//
|
|
125
|
-
|
|
126
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|