Skip to content

Commit ceae1ec

Browse files
authored
fix: Static props prevents selectors recall [2] (#102)
* add failing test * simplify the test * minor adjustment * fix it, but not super confident * update CHANGELOG
1 parent 534afa9 commit ceae1ec

File tree

3 files changed

+60
-11
lines changed

3 files changed

+60
-11
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Changed
66

77
- fix: Static props prevents selectors recall #101
8+
- fix: Static props prevents selectors recall [2] #102
89

910
## [3.0.0] - 2024-05-02
1011

src/memoize.ts

+12-9
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,18 @@ type Used = {
2121
};
2222
type Affected = WeakMap<object, Used>;
2323

24-
const trackMemoOriginalObjSet = new WeakSet<object>();
24+
const trackMemoUntrackedObjSet = new WeakSet<object>();
2525

2626
const isObject = (x: unknown): x is object =>
2727
typeof x === 'object' && x !== null;
2828

2929
const untrack = <T>(x: T, seen: WeakSet<object>): T => {
3030
if (!isObject(x)) return x;
31-
const originalObj = getUntracked(x);
32-
if (originalObj !== null) {
31+
const untrackedObj = getUntracked(x);
32+
if (untrackedObj) {
3333
trackMemo(x);
34-
trackMemoOriginalObjSet.add(originalObj);
35-
return originalObj;
34+
trackMemoUntrackedObjSet.add(untrackedObj);
35+
return untrackedObj;
3636
}
3737
if (!seen.has(x)) {
3838
seen.add(x);
@@ -46,11 +46,14 @@ const untrack = <T>(x: T, seen: WeakSet<object>): T => {
4646

4747
const touchAffected = (dst: unknown, src: unknown, affected: Affected) => {
4848
if (!isObject(dst) || !isObject(src)) return;
49-
if (trackMemoOriginalObjSet.has(getUntracked(src) as never)) {
50-
trackMemo(dst);
49+
const untrackedObj = getUntracked(src);
50+
const used = affected.get(untrackedObj || src);
51+
if (!used) {
52+
if (trackMemoUntrackedObjSet.has(untrackedObj as never)) {
53+
trackMemo(dst);
54+
}
55+
return;
5156
}
52-
const used = affected.get(getUntracked(src) || src);
53-
if (!used) return;
5457
used[HAS_KEY_PROPERTY]?.forEach((key) => {
5558
Reflect.has(dst, key);
5659
});

tests/issue_100.spec.ts

+47-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe('Static props prevents selectors recall (#100)', () => {
2424
},
2525
};
2626

27-
const selectAllBooks = memoize((state: State) => Object.values(state));
27+
const selectBook1 = memoize((state: State) => state.book1);
2828

2929
const selectPriceString = memoize(
3030
(state: State) => state.book1.priceString,
@@ -36,7 +36,52 @@ describe('Static props prevents selectors recall (#100)', () => {
3636
return priceString;
3737
});
3838

39-
selectAllBooks(state1);
39+
selectBook1(state1);
40+
41+
expect(selectAdjustedPriceString(state1)).toBe('10');
42+
expect(selectAdjustedPriceString(state2)).toBe('20');
43+
});
44+
45+
it('case 2', () => {
46+
type State = {
47+
book1: {
48+
staticProp: string;
49+
priceString: string;
50+
};
51+
};
52+
53+
const state1: State = {
54+
book1: {
55+
staticProp: '5',
56+
priceString: '10',
57+
},
58+
};
59+
60+
const state2: State = {
61+
book1: {
62+
staticProp: '5',
63+
priceString: '20',
64+
},
65+
};
66+
67+
const selectBook1 = memoize((state: State) => state.book1);
68+
69+
const selectPriceString = memoize(
70+
(state: State) => state.book1.priceString,
71+
);
72+
73+
const selectAdjustedPriceString = memoize((state: State) => {
74+
const priceString = selectPriceString(state);
75+
state.book1.staticProp; // touch the prop
76+
return priceString;
77+
});
78+
79+
const selectMemoizedPriceString = memoize((state: State) =>
80+
selectPriceString(state),
81+
);
82+
83+
selectBook1(state1);
84+
selectMemoizedPriceString(state1);
4085

4186
expect(selectAdjustedPriceString(state1)).toBe('10');
4287
expect(selectAdjustedPriceString(state2)).toBe('20');

0 commit comments

Comments
 (0)