Обновление: элегантное решение (любезно предоставлено Крисом Пруденом и Синдром Сорхусом)
Решение основано на этом коммите Криса в библиотеке 'on-change' Синдре .
Объяснение решения , Крис:
В ловушке set
моя цель - определить, является ли предоставленное значение Proxy
произведено предыдущим вызовом ловушки get
.Если это Proxy
, то любой доступ к свойству будет перехвачен нашей собственной get
ловушкой.Поэтому, когда я получу доступ к value[proxyTarget]
, будет вызвана наша get
ловушка, которая закодирована так, чтобы возвращать target
при property === proxyTarget
(строка 46).Если значение, переданное set
, не является прокси-сервером, созданным при изменении, то value[proxyTarget]
- это undefined
.
Полный код решения:
(object, onChange) => {
let inApply = false;
let changed = false;
function handleChange() {
if (!inApply) {
onChange();
} else if (!changed) {
changed = true;
}
}
const handler = {
get(target, property, receiver) {
const descriptor = Reflect.getOwnPropertyDescriptor(target, property);
const value = Reflect.get(target, property, receiver);
// Preserve invariants
if (descriptor && !descriptor.configurable) {
if (descriptor.set && !descriptor.get) {
return undefined;
}
if (descriptor.writable === false) {
return value;
}
}
try {
return new Proxy(value, handler);
} catch (_) {
return value;
}
},
set(target, property, value) {
const result = Reflect.set(target, property, value);
handleChange();
return result;
},
defineProperty(target, property, descriptor) {
const result = Reflect.defineProperty(target, property, descriptor);
handleChange();
return result;
},
deleteProperty(target, property) {
const result = Reflect.deleteProperty(target, property);
handleChange();
return result;
},
apply(target, thisArg, argumentsList) {
if (!inApply) {
inApply = true;
const result = Reflect.apply(target, thisArg, argumentsList);
if (changed) {
onChange();
}
inApply = false;
changed = false;
return result;
}
return Reflect.apply(target, thisArg, argumentsList);
}
};
return new Proxy(object, handler);
};
Это решило мою проблему, не прибегая к хакерской проверке методов изменения массива.
Исходное решение:
Я уже разобрался с помощью Пост Дэвида Уолша здесь .Это все еще безобразно, но работает на данный момент.
Обновлен onChanged
Proxy Maker с рекурсивным прерыванием get
.
get: function (target, property, receiver) {
let retval;
try {
retval = new Proxy(target[property], handler);
} catch (err) {
retval = Reflect.get(target, property, receiver);
}
if (mutators.includes(property))
onChange(target, property, receiver);
return retval;
},
Также добавлен список функций для проверкиполучить ловушку против (уродливого, хакерского бита):
const mutators = [
"push",
"pop",
"shift",
"unshift",
"splice",
"reverse",
"fill",
"sort"
]
Это, кажется, работает в моем тестировании до сих пор.
Спасибо за указание в правильном направлении.