dai-shi / proxy-compare

Compare two objects using accessed properties with Proxy
https://www.npmjs.com/package/proxy-compare
MIT License
285 stars 18 forks source link

Compare with Proxy issue #51

Closed zslucky closed 1 year ago

zslucky commented 1 year ago

em... another interest thing.... show the code first.

const state = {
  age: 10,
  gender: 'male',
}
const getters = {
  isMale: memo((state, getters) => state.gender === 'male'),
  isMaleOldThan20: memo((state, getters) => getters.isMale && state.age > 20),
  isMaleOldThan20AndLessThan50: memo((state, getters) => getters.isMaleOldThan20 &&  state.age < 50),
};

getters is a proxy object which I add some logic and turn function to value, so the statement above can run correctly. And memo function like the other project you create proxy-memorize 's memoWithArgs, but did a little changes.

The interest thing is that, the Effected WeakMap flatten the keys, when I called getters.isMaleOldThan20AndLessThan50 from other side, Effected will store the getters 's proxy as key, isMaleOldThan20 as value, then isMaleOldThan20 will trigger it's function call, and Effected will store the getters 's proxy as key, and isMale as value, this action will override the prev key, then the cache may invalid.

I'm not sure, but Effected WeakMap may can store the call order with deep a weakmap? something like follow:

const effected = new WeakMap();
effected.set([state, getters], ['length', 0, 1])
effected.set(state, ['age'])
effected.set(getters, [{ key: 'isMaleOldThan20', new WeakMap().set(getters, ['isMale']) }])
dai-shi commented 1 year ago

Not sure, but sounds like there's some misuse or misconception. Can you reproduce the issue only with proxy-compare and as minimal as possible?

zslucky commented 1 year ago

OK, I will do a deep investigation, and provide a code example.

zslucky commented 1 year ago

Hi, @dai-shi , After a deep investigation, I found the root cause, pls refer to this Demo

I extract some main logic from my lib, it may not a reasonable code, but can show the issue.

The root cause is isChanged function read prevObj and nextObj properties in real-time, when the obj is a proxy object or a function property which contains global values or other closure values, it may invalid.

So what I mean is maybe we can store the effect key with its value together, then in isChanged function, we can read prevObj value correctly at that time from the stored site, not run at real-time.

dai-shi commented 1 year ago

Okay, so you have a misconception about this library. (That's understandable because readme is empty.)

It's developed for immutable state model. It's the contract you need to update the whole object if you change a part.

So, this works:

const s = { c: 0 };
const a = new WeakMap();
const p = createProxy(s, a);
p.c
s2 = { c: 1 };
isChanged(s, s2, a); // is true

But, this does not:

const s = { c: 0 };
const a = new WeakMap();
const p = createProxy(s, a);
p.c
++s.c;
isChanged(s, s, a); // is false

Especially because it's checking the referential equality: https://github.com/dai-shi/proxy-compare/blob/b8110983e440328b884f3582943b77a38de1335d/src/index.ts#L280-L282

We have no plan to support mutable state/object in this library. If you are interested in using mutable state, check out valtio.