-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Skip patching for Array-input patch-scopes, replace instead #21
Comments
Would love to see patchinko in combination with atama (proxy based change management) Engine: See how the proxify method proxies each part of the state recursively... thus also handling array in an elegant non-intrusive way that "just works" if (Array.isArray(value)) {
value = value.map((value, property, target) => {
return proxify(value, [...stack, { target, property, value }]);
});
} |
To make Atama configurable to suit your valid scenario of having the array as "one logical unit", we could make the Atama engine configurable with factory functions: const $createProxify = target => {
return (value, stack) => {
if (basicTypes.includes(typeof value) || value === null) {
return value;
}
if (Array.isArray(value)) {
value = value.map((value, property, target) => {
return proxify(value, [...stack, { target, property, value }]);
});
}
if (/^\{/.test(JSON.stringify(value))) {
for (let property in value) {
const current = { target, property, value: value[property] };
value[property] = proxify(value[property], [...stack, current]);
}
}
return new Proxy(value, {
get: getProxy(stack),
set: setProxy(stack),
deleteProperty: delProxy(stack)
});
};
};
// Set values
const createSetProxy = (options = {}) => {
const createProxify = options.createProxify || $createProxify;
return (stack = []) => (target, property, value) => {
// Log it into the history
const type = typeof target[property] === "undefined" ? "create" : "update";
history.add({ type, key: getKey([...stack, property]), value });
// First of all set it in the beginning
target[property] = value;
const proxify = createProxify(target, options);
// Proxify the value in-depth
target[property] = proxify(value, [...stack, { target, property, value }]);
// Trigger the root listener for any change
listeners.forEach(one => one(state));
return true;
};
};
const $createState = (options = {}) => {
return new Proxy(
{},
{
get: getProxy(),
set: createSetProxy(options),
deleteProperty: delProxy()
}
);
};
const createEngine = (options = {}) => {
const engine = {};
const createState = options.createState || $createState;
const state = createState(options);
engine.attach = arg => {
if (detached.length) {
listeners.push(...detached.splice(0, detached.length));
}
return state;
};
engine.detatch = temp => {
detached.push(...listeners.splice(0, listeners.length));
// TODO: build the raw tree here
return state;
};
engine.listen = cb => listeners.push(cb);
};
export default createEngine; Then we can supply our own |
Made it a little easier to customize: const $isPrimitive = value =>
basicTypes.includes(typeof value) || value === null;
const createProxifyPrimitive = (options = {}) => {
const isPrimitive = options.isPrimitive || $isPrimitive;
return value => {
if (!isPrimitive) return;
return value;
};
};
const isArray = !Array.isArray(value);
const $proxifyValues = (value, stack) => {
return value.map((value, property, target) => {
return proxify(value, [...stack, { target, property, value }]);
});
};
const createProxifyArray = (options = {}) => {
const proxifyValues = options.proxifyValues || $proxifyValues;
return (value, stack) => {
if (!isArray(value)) return;
return proxifyValues(value, stack);
};
};
const isJson = value => /^\{/.test(JSON.stringify(value));
const $proxifyJsonValues = (value, stack) => {
for (let property in value) {
const current = { target, property, value: value[property] };
value[property] = proxify(value[property], [...stack, current]);
}
};
const createProxifyJson = (options = {}) => {
const proxifyJsonValues = options.proxifyJsonValues || $proxifyJsonValues;
return value => {
if (!isJson(value)) return;
proxifyJsonValues(value);
};
};
const createProxifyComplex = (options = {}) => {
const createProxy = options.createProxy || $createProxy;
return (value, stack, options) => {
const { createProxifyJson } = Object.assign(typeProxifiers, options);
const proxifyJson = createProxifyJson(options);
proxifyJson(value);
return createProxy(value, stack, options);
};
};
const typeProxifiers = {
createProxifyPrimitive,
createProxifyArray,
createProxifyJson,
createProxifyComplex
};
const $createProxy = (value, stack, options) => {
return new Proxy(value, {
get: getProxy(stack),
set: createSetProxy(stack, options),
deleteProperty: delProxy(stack)
});
};
const $createProxify = (options = {}) => {
const {
createProxifyPrimitive,
createProxifyArray,
createProxifyComplex
} = Object.assign(typeProxifiers, options);
const proxifyPrimitive = createProxifyPrimitive(options);
const proxifyArray = createProxifyArray(options);
const proxifyComplex = createProxifyComplex(options);
return (value, stack) => {
return (
proxifyPrimitive(value) ||
proxifyArray(value, options) ||
proxifyComplex(value, stack, options)
);
};
};
// Set values
const createSetProxy = (options = {}) => {
const createProxify = options.createProxify || $createProxify;
return (stack = []) => (target, property, value) => {
// Log it into the history
const type = typeof target[property] === "undefined" ? "create" : "update";
history.add({ type, key: getKey([...stack, property]), value });
// First of all set it in the beginning
target[property] = value;
const proxify = createProxify(options);
// Proxify the value in-depth
target[property] = proxify(value, [...stack, { target, property, value }]);
// Trigger the root listener for any change
listeners.forEach(one => one(state));
return true;
};
};
const $createState = (options = {}) => {
const createProxy = options.createProxy || $createProxy;
return createProxy({}, undefined, options);
};
const createEngine = (options = {}) => {
const engine = {};
const createState = options.createState || $createState;
const state = createState(options);
engine.attach = arg => {
if (detached.length) {
listeners.push(...detached.splice(0, detached.length));
}
return state;
};
engine.detatch = temp => {
detached.push(...listeners.splice(0, listeners.length));
// TODO: build the raw tree here
return state;
};
engine.listen = cb => listeners.push(cb);
};
export default createEngine; To customize using array as single entity createEngine({
proxifyValues: (value) => value
}) Otherwise each value in the array is proxified as well |
The kitchen sink demo shows a contrived example whereby within constant mode we use patch-scope to copy then patch an array using a numerically-keyed object.
At the time this was meant to illustrate the low-level simplicity of the underlying algorithm, but when using Patchinko in flavoured mode (constant or immutable), for the purposes of holistic state management, this makes no sense at all.
Lists in application data models tend to be holistic entities: the idea that one would simply merge a short array into a longer array would be a very particular use case; far more intuitive that one would want to replace the array contents wholesale, with immutable replacing the array itself and constant simply splicing out every item.
Before: treat arrays as hashes, iterate over input and patch corresponding keys
After: immutable: replace arrays; constant:
target.splice(0, Infinity, input)
The text was updated successfully, but these errors were encountered: