reactive-react-redux
Version:
React Redux binding with React Hooks and Proxy
198 lines (156 loc) • 5.79 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getUntrackedObject = exports.trackMemo = exports.isDeepChanged = exports.createDeepProxy = void 0;
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
// -------------------------------------------------------
// deep proxy
// -------------------------------------------------------
var OWN_KEYS_SYMBOL = Symbol('OWN_KEYS');
var TRACK_MEMO_SYMBOL = Symbol('TRACK_MEMO');
var GET_ORIGINAL_SYMBOL = Symbol('GET_ORIGINAL'); // check if obj is a plain object or an array
var isPlainObject = function isPlainObject(obj) {
try {
var proto = Object.getPrototypeOf(obj);
return proto === Object.prototype || proto === Array.prototype;
} catch (e) {
return false;
}
}; // copy obj if frozen
var unfreeze = function unfreeze(obj) {
if (!Object.isFrozen(obj)) return obj;
if (Array.isArray(obj)) {
return Array.from(obj);
}
return Object.assign({}, obj);
};
var createProxyHandler = function createProxyHandler() {
return {
recordUsage: function recordUsage(key) {
if (this.trackObj) return;
var used = this.affected.get(this.originalObj);
if (!used) {
used = new Set();
this.affected.set(this.originalObj, used);
}
used.add(key);
},
recordObjectAsUsed: function recordObjectAsUsed() {
this.trackObj = true;
this.affected["delete"](this.originalObj);
},
get: function get(target, key) {
if (key === GET_ORIGINAL_SYMBOL) {
return this.originalObj;
}
this.recordUsage(key); // eslint-disable-next-line no-use-before-define, @typescript-eslint/no-use-before-define
return createDeepProxy(target[key], this.affected, this.proxyCache);
},
has: function has(target, key) {
if (key === TRACK_MEMO_SYMBOL) {
this.recordObjectAsUsed();
return true;
} // LIMITATION:
// We simply record the same as get.
// This means { a: {} } and { a: {} } is detected as changed,
// if 'a' in obj is handled.
this.recordUsage(key);
return key in target;
},
ownKeys: function ownKeys(target) {
this.recordUsage(OWN_KEYS_SYMBOL);
return Reflect.ownKeys(target);
}
};
};
var createDeepProxy = function createDeepProxy(obj, affected, proxyCache) {
if (!isPlainObject(obj)) return obj;
var origObj = obj[GET_ORIGINAL_SYMBOL]; // unwrap proxy
if (origObj) obj = origObj;
var proxyHandler = proxyCache && proxyCache.get(obj);
if (!proxyHandler) {
proxyHandler = createProxyHandler();
proxyHandler.proxy = new Proxy(unfreeze(obj), proxyHandler);
proxyHandler.originalObj = obj;
proxyHandler.trackObj = false; // for trackMemo
if (proxyCache) {
proxyCache.set(obj, proxyHandler);
}
}
proxyHandler.affected = affected;
proxyHandler.proxyCache = proxyCache;
return proxyHandler.proxy;
};
exports.createDeepProxy = createDeepProxy;
var isOwnKeysChanged = function isOwnKeysChanged(origObj, nextObj) {
var origKeys = Reflect.ownKeys(origObj);
var nextKeys = Reflect.ownKeys(nextObj);
return origKeys.length !== nextKeys.length || origKeys.some(function (k, i) {
return k !== nextKeys[i];
});
};
var isDeepChanged = function isDeepChanged(origObj, nextObj, affected, cache, assumeChangedIfNotAffected) {
if (origObj === nextObj) return false;
if (_typeof(origObj) !== 'object' || origObj === null) return true;
if (_typeof(nextObj) !== 'object' || nextObj === null) return true;
var used = affected.get(origObj);
if (!used) return !!assumeChangedIfNotAffected;
if (cache) {
var hit = cache.get(origObj);
if (hit && hit.nextObj === nextObj) {
return hit.changed;
} // for object with cycles (changed is `undefined`)
cache.set(origObj, {
nextObj: nextObj
});
}
var changed = null; // eslint-disable-next-line no-restricted-syntax
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = used[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var key = _step.value;
var c = key === OWN_KEYS_SYMBOL ? isOwnKeysChanged(origObj, nextObj) : isDeepChanged(origObj[key], nextObj[key], affected, cache, assumeChangedIfNotAffected !== false);
if (typeof c === 'boolean') changed = c;
if (changed) break;
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator["return"] != null) {
_iterator["return"]();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
if (changed === null) changed = !!assumeChangedIfNotAffected;
if (cache) {
cache.set(origObj, {
nextObj: nextObj,
changed: changed
});
}
return changed;
}; // explicitly track object with memo
exports.isDeepChanged = isDeepChanged;
var trackMemo = function trackMemo(obj) {
if (isPlainObject(obj)) {
return TRACK_MEMO_SYMBOL in obj;
}
return false;
}; // get original object from proxy
exports.trackMemo = trackMemo;
var getUntrackedObject = function getUntrackedObject(obj) {
if (isPlainObject(obj)) {
return obj[GET_ORIGINAL_SYMBOL] || null;
}
return null;
};
exports.getUntrackedObject = getUntrackedObject;