UNPKG

respondix

Version:

Tiny lightweight proxy-based and zero-dependency reactivity library

122 lines 4.73 kB
var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; import Observable from './observable'; import { mapObject } from './utils'; import { watch } from './job'; export function defineReactive(target, name, getter) { watch(function () { return target[name] = getter(); }); } export default function makeReactive(state) { // @ts-ignore if (state === null || state === void 0 ? void 0 : state.__isProxy) return state; // Already reactive if (Array.isArray(state)) { // @ts-ignore return makeArrayReactive(state); } else if (state instanceof Function) { // Don't proxy functions return state; } else if (state instanceof Object) { return makeObjectReactive(state); } else { // Must be primitive. Return as-is return state; } } export function unwrap(state) { if (!state.__isProxy) return state; // Passed state not reactive. Return original object // Return shallow clone of object. This breaks reactivity return __assign({}, state); } export function deepUnwrap(state) { // Recursively unwrap object return mapObject(unwrap(state), deepUnwrap); } function makeObjectReactive(obj) { // Map to hold observables, which in turn hold subscribed jobs, for each property var observables = new Map(); // Recursively create proxies for object and nested objects return new Proxy(mapObject(obj, function (p) { return makeReactive(p); }), { get: function (target, key, receiver) { // Hard-coded property to identify reactive state if (key === '__isProxy') return true; // Create observable if not already present, add running job as subscriber if (observables.has(key)) observables.get(key).depend(); else observables.set(key, new Observable().depend()); // Return requested value. If method, bind to proxy in order to detect mutations return typeof target[key] === 'function' ? target[key].bind(receiver) : target[key]; }, set: function (target, key, value) { var _a; // Exit if mutation has no effect if (target[key] === value) return true; // Assign reactive version of value target[key] = makeReactive(value); // Notify subscribers, if any (_a = observables.get(key)) === null || _a === void 0 ? void 0 : _a.notify(); return true; }, deleteProperty: function (target, key) { var _a; if (!(key in target)) return false; // Delete value. Notify subscribers delete target[key]; (_a = observables.get(key)) === null || _a === void 0 ? void 0 : _a.notify(); return true; } }); } function makeArrayReactive(arr) { // Array only needs one observable since indices are not bound to specific items var observable = new Observable(); // Create proxy for clone of array with reactive items return new Proxy(arr.map(function (i) { return makeReactive(i); }), { get: function (target, key, receiver) { // Hard-coded property to identify reactive state if (key === '__isProxy') return true; // Add running job to subscribers observable.depend(); // Return requested value. If method, bind to proxy in order to detect mutations. // Even works for built-ins like push and pop. Scripting-language magic return typeof target[key] === 'function' ? target[key].bind(receiver) : target[key]; }, set: function (target, key, value) { // Exit if mutation has no effect if (target[key] === value) return true; // Assign reactive version of value target[key] = makeReactive(value); // Notify subscribers observable.notify(); return true; }, deleteProperty: function (target, key) { if (!(key in target)) return false; // Delete value. Notify subscribers delete target[key]; observable.notify(); return true; } }); } //# sourceMappingURL=state.js.map