Skip to content

Commit 86a5f4f

Browse files
committed
fix: hydrate rows subscribed in listeners
1 parent f835c9a commit 86a5f4f

File tree

17 files changed

+130
-81
lines changed

17 files changed

+130
-81
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@amos/root",
33
"private": true,
4-
"version": "0.3.0-beta.32",
4+
"version": "0.3.0-beta.33",
55
"license": "MIT",
66
"workspaces": [
77
"packages/*",

packages/amos-babel/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "amos-babel",
3-
"version": "0.3.0-beta.32",
3+
"version": "0.3.0-beta.33",
44
"private": true,
55
"files": [],
66
"peerDependencies": {

packages/amos-boxes/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "amos-boxes",
3-
"version": "0.3.0-beta.32",
3+
"version": "0.3.0-beta.33",
44
"private": true,
55
"files": []
66
}

packages/amos-core/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "amos-core",
3-
"version": "0.3.0-beta.32",
3+
"version": "0.3.0-beta.33",
44
"private": true,
55
"files": []
66
}

packages/amos-io/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "amos-io",
3-
"version": "0.3.0-beta.32",
3+
"version": "0.3.0-beta.33",
44
"private": true,
55
"files": []
66
}

packages/amos-persist/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "amos-persist",
3-
"version": "0.3.0-beta.32",
3+
"version": "0.3.0-beta.33",
44
"private": true,
55
"files": []
66
}

packages/amos-persist/src/enhancer.spec.ts

+33-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ describe('withPersist', () => {
7575
]);
7676
const r2 = store.select(userMapBox.getItem(Morty.id));
7777
expect(r2).toBe(Morty);
78-
await sleep(1);
78+
await store.dispatch(hydrate([]));
7979
const r3 = store.select(userMapBox.getItem(Morty.id));
8080
expect(r3).toEqual(Morty.set('firstName', 'F2'));
8181
expectCalledWith(getMulti, [[toKey(userMapBox, Morty.id)]]);
@@ -112,4 +112,36 @@ describe('withPersist', () => {
112112
const r5 = store.select(darkModeBox);
113113
expect([r4, r5]).toEqual([false, 3 * 2 + 1]);
114114
});
115+
116+
it('should not hydrate non-selected row', async () => {
117+
const storage = new MemoryStorage();
118+
await storage.setMulti([
119+
[toKey(countBox), 1, 1],
120+
[toKey(userMapBox, Rick.id), 1, Rick.set('firstName', 'F1')],
121+
]);
122+
const store = createStore(void 0, withPersist({ storage, includes: () => true }));
123+
store.select(countBox);
124+
await store.dispatch(hydrate([]));
125+
expect(store.select(userMapBox.getItem(Rick.id)).firstName).toBe(Rick.firstName);
126+
});
127+
128+
it('should hydrate rows selected in listener', async () => {
129+
const storage = new MemoryStorage();
130+
await storage.setMulti([
131+
[toKey(countBox), 1, 1],
132+
[toKey(userMapBox, Rick.id), 1, Rick.set('firstName', 'F1')],
133+
]);
134+
const store = createStore(void 0, withPersist({ storage, includes: () => true }));
135+
store.subscribe(() => {
136+
if (store.select(countBox) === 1) {
137+
store.select(userMapBox.getItem(Rick.id));
138+
}
139+
});
140+
store.select(countBox);
141+
await store.dispatch(hydrate([]));
142+
expect(store.select(countBox)).toBe(1);
143+
expect(store.select(userMapBox.getIn(Rick.id, 'firstName'))).toBe(Rick.firstName);
144+
await store.dispatch(hydrate([]));
145+
expect(store.select(userMapBox.getIn(Rick.id, 'firstName'))).toBe('F1');
146+
});
115147
});

packages/amos-persist/src/enhancer.ts

+21-11
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
* @author junbao <[email protected]>
44
*/
55

6-
import { Box, type Selectable, type Selector, StoreEnhancer } from 'amos-core';
7-
import { append, isAmosObject, once, override, PartialRequired } from 'amos-utils';
6+
import { Box, type Mutation, type Selectable, type Selector, StoreEnhancer } from 'amos-core';
7+
import { append, isAmosObject, once, override, PartialRequired, StackObserver } from 'amos-utils';
88
import { createHydrate } from './hydrate';
99
import { createPersist } from './persist';
1010
import { persistBox, type PersistState } from './state';
@@ -23,24 +23,19 @@ export function withPersist(options: PartialRequired<PersistOptions, 'storage'>)
2323
const hydrate = createHydrate(store, finalOptions);
2424
const persist = createPersist(store, finalOptions);
2525

26+
const selecting = new StackObserver();
2627
const initial = new Map<string, any>();
2728

2829
const state: PersistState = {
2930
...finalOptions,
30-
selecting: false,
3131
init: once(async () => options.storage.init?.()),
3232
snapshot: new Map(),
33+
select: selecting.observe((s: any) => store.select(s)),
3334
getInitial: (box) => {
3435
if (initial.has(box.key)) {
3536
return initial.get(box.key);
3637
}
37-
const selecting = state.selecting;
38-
state.selecting = true;
39-
try {
40-
store.select(box);
41-
} finally {
42-
state.selecting = selecting;
43-
}
38+
state.select(box);
4439
return state.getInitial(box);
4540
},
4641
hydrate,
@@ -52,6 +47,21 @@ export function withPersist(options: PartialRequired<PersistOptions, 'storage'>)
5247
initial.set(box.key, initialState);
5348
});
5449

50+
let dispatchingMutation = 0;
51+
override(store, 'dispatch', (dispatch) => {
52+
return (dispatchables: any) => {
53+
if (!isAmosObject<Mutation>(dispatchables, 'mutation')) {
54+
return dispatch(dispatchables);
55+
}
56+
try {
57+
dispatchingMutation++;
58+
return dispatch(dispatchables);
59+
} finally {
60+
dispatchingMutation--;
61+
}
62+
};
63+
});
64+
5565
let selectingRows: PersistRowKey<any> | undefined = void 0;
5666
override(store, 'select', (select) => {
5767
return (selectable: any): any => {
@@ -64,7 +74,7 @@ export function withPersist(options: PartialRequired<PersistOptions, 'storage'>)
6474
}
6575
try {
6676
const r = select(selectable as Selectable);
67-
if (state.selecting) {
77+
if (selecting.count || dispatchingMutation) {
6878
return r;
6979
}
7080
// hydrate rows for multi-row boxes

packages/amos-persist/src/hydrate.ts

+47-55
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import { Box, type Mutation, type Store } from 'amos-core';
7-
import { fromJS, type ID, isArray, must, nextSerialTicker, tryFinally } from 'amos-utils';
7+
import { fromJS, type ID, isArray, must, nextSerialTicker } from 'amos-utils';
88
import { persistBox } from './state';
99
import type { PersistEntry, PersistKey, PersistOptions } from './types';
1010
import { fromKey, toKey, toPersistOptions } from './utils';
@@ -70,63 +70,55 @@ export const createHydrate = (store: Store, finalOptions: PersistOptions) => {
7070
exactKeys.length ? state.storage.getMulti(exactKeys.map(([k, i]) => toKey(k, i))) : [],
7171
]);
7272

73-
tryFinally(
74-
() => {
75-
state.selecting = true;
76-
const tablePrefixMap: Record<string, PersistEntry[]> = {};
77-
const exactEntries: Mutation[] = [];
78-
exacts.forEach((value, index) => {
79-
if (value === null) {
80-
return;
81-
}
82-
const [key, rowId] = exactKeys[index];
83-
const box = Box.get(key);
84-
if (rowId !== void 0) {
85-
if (!tablePrefixMap[key]) {
86-
tablePrefixMap[key] = [];
87-
prefixBoxes.push(key);
88-
prefixes.push(tablePrefixMap[key]);
73+
const tablePrefixMap: Record<string, PersistEntry[]> = {};
74+
const exactEntries: Mutation[] = [];
75+
exacts.forEach((value, index) => {
76+
if (value === null) {
77+
return;
78+
}
79+
const [key, rowId] = exactKeys[index];
80+
const box = Box.get(key);
81+
if (rowId !== void 0) {
82+
if (!tablePrefixMap[key]) {
83+
tablePrefixMap[key] = [];
84+
prefixBoxes.push(key);
85+
prefixes.push(tablePrefixMap[key]);
86+
}
87+
tablePrefixMap[key].push([toKey(key, rowId), ...value]);
88+
} else if (state.select(box) === state.getInitial(box)) {
89+
const js = migrate(box, value[0], '', value[1]);
90+
if (js !== void 0) {
91+
exactEntries.push(box.setState(fromJS(state.getInitial(box), js)));
92+
}
93+
}
94+
});
95+
96+
store.dispatch(
97+
prefixes
98+
.map((p, i): Mutation[] => {
99+
const box = Box.get(prefixBoxes[i]);
100+
const curr = state.select(box);
101+
const data: Record<string, any> = {};
102+
for (let [k, v, d] of p) {
103+
const id = fromKey(k);
104+
if (id === void 0) {
105+
continue;
106+
}
107+
if (box.table!.getRow(curr, id) !== box.table!.getRow(state.getInitial(box), id)) {
108+
continue;
89109
}
90-
tablePrefixMap[key].push([toKey(key, rowId), ...value]);
91-
} else if (store.select(box) === state.getInitial(box)) {
92-
const js = migrate(box, value[0], '', value[1]);
93-
if (js !== void 0) {
94-
exactEntries.push(box.setState(fromJS(box.getInitialState(), js)));
110+
d = migrate(box, v, id, d);
111+
if (d !== void 0) {
112+
data[id] = d;
95113
}
96114
}
97-
});
98-
99-
store.dispatch(
100-
prefixes
101-
.map((p, i): Mutation[] => {
102-
const box = Box.get(prefixBoxes[i]);
103-
const curr = store.select(box);
104-
const data: Record<string, any> = {};
105-
for (let [k, v, d] of p) {
106-
const id = fromKey(k);
107-
if (id === void 0) {
108-
continue;
109-
}
110-
if (box.table!.getRow(curr, id) !== box.table!.getRow(state.getInitial(box), id)) {
111-
continue;
112-
}
113-
d = migrate(box, v, id, d);
114-
if (d !== void 0) {
115-
data[id] = d;
116-
}
117-
}
118-
if (Object.keys(data).length === 0) {
119-
return [];
120-
}
121-
return [box.setState(box.table!.hydrate(curr, data))];
122-
})
123-
.flat()
124-
.concat(exactEntries),
125-
);
126-
},
127-
() => {
128-
state.selecting = false;
129-
},
115+
if (Object.keys(data).length === 0) {
116+
return [];
117+
}
118+
return [box.setState(box.table!.hydrate(curr, data))];
119+
})
120+
.flat()
121+
.concat(exactEntries),
130122
);
131123
}, finalOptions.onError);
132124
};

packages/amos-persist/src/state.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { toKey } from './utils';
1010

1111
export interface PersistState extends PersistOptions {
1212
init: () => Promise<void>;
13-
selecting: boolean;
13+
select: Select;
1414
getInitial: (box: Box) => any;
1515
snapshot: Map<string, any>;
1616
hydrate: NextTicker<PersistKey<any>, void>;

packages/amos-react/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "amos-react",
3-
"version": "0.3.0-beta.32",
3+
"version": "0.3.0-beta.33",
44
"private": true,
55
"files": [],
66
"peerDependencies": {

packages/amos-shapes/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "amos-shapes",
3-
"version": "0.3.0-beta.32",
3+
"version": "0.3.0-beta.33",
44
"private": true,
55
"files": []
66
}

packages/amos-testing/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "amos-testing",
3-
"version": "0.3.0-beta.32",
3+
"version": "0.3.0-beta.33",
44
"private": true,
55
"files": []
66
}

packages/amos-typescript/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "amos-typescript",
3-
"version": "0.3.0-beta.32",
3+
"version": "0.3.0-beta.33",
44
"private": true,
55
"files": [],
66
"peerDependencies": {

packages/amos-utils/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "amos-utils",
3-
"version": "0.3.0-beta.32",
3+
"version": "0.3.0-beta.33",
44
"private": true,
55
"files": []
66
}

packages/amos-utils/src/misc.ts

+16-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import { isIterable } from './equals';
7-
import { ValueOrConstructor, ValueOrFunc } from './types';
7+
import { type AnyFunc, ValueOrConstructor, ValueOrFunc } from './types';
88

99
export function must<T>(value: T, message: string): asserts value {
1010
if (!value) {
@@ -51,6 +51,21 @@ export function toArray<T>(items: T[] | Iterable<T> | T): T[] {
5151
return [items as T];
5252
}
5353

54+
export class StackObserver {
55+
count = 0;
56+
57+
observe<T extends AnyFunc>(fn: T) {
58+
return ((...args: any[]) => {
59+
this.count++;
60+
try {
61+
return fn(...args);
62+
} finally {
63+
this.count--;
64+
}
65+
}) as T;
66+
}
67+
}
68+
5469
export interface NextTicker<T, R> {
5570
(...items: T[]): void;
5671
wait(...items: T[]): Promise<R>;

packages/amos/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "amos",
3-
"version": "0.3.0-beta.32",
3+
"version": "0.3.0-beta.33",
44
"description": "An out-of-the-box state management library designed for your large-scale projects.",
55
"keywords": [
66
"amos",

0 commit comments

Comments
 (0)