/* Helper functions that can be used in componentDidUpdate to find what changed between component updates.
Example: componentDidUpdate(prevProps, prevState) {
const propsChanged = changes(prevProps, this.props);
const stateChanged = changes(prevState, this.state);
const propsDiff = diff(prevProps, this.props);
const stateDiff = diff(prevState, this.state);
if (propsChanged.someDataThing) {
this.doStuff(propsDiff.someDataThing);
}
if (stateChanged.intermediateValue) {
this.continueThing();
}
}
*/
// Copyright (c) 2021 Jonathan Voss
// BSD0 license
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
// TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
// NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
// IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// compares two objects and returns recursive boolean flags for what properties are different
// flags will not appear for any non-array without a difference in content
// flag will be false for two equivalent arrays that are different instances
export function changes (obj1, obj2) {
let flags = {};
Object.keys(obj2).forEach(k => {
if ((!obj1[k] && !!obj2[k]) || obj1[k] !== obj2[k]) {
if (typeof obj1[k] == 'undefined' || typeof obj2[k] == 'undefined' || obj1[k] == null || obj2[k] == null || obj1[k].constructor.name !== obj2[k].constructor.name) {
flags[k] = true;
} else {
switch (obj1[k].constructor.name) {
case 'Array':
flags[k] = (obj2[k].constructor.name == 'Array') ? obj1[k].filter(x => !obj2[k].includes(x)).length > 0 : true;
break;
case 'Object':
flags[k] = changes(obj1[k], obj2[k]);
break;
default:
flags[k] = true;
}
}
}
});
Object.keys(obj1).forEach(k => {
if ((!!obj1[k] && !obj2[k]) || obj1[k] !== obj2[k]) {
if (typeof obj1[k] == 'undefined' || typeof obj2[k] == 'undefined' || obj1[k] == null || obj2[k] == null || obj1[k].constructor.name !== obj2[k].constructor.name) {
flags[k] = true;
} else {
switch (obj2[k].constructor.name) {
case 'Array':
flags[k] = (obj1[k].constructor.name == 'Array') ? obj2[k].filter(x => !obj1[k].includes(x)).length > 0 : true;
break;
case 'Object':
flags[k] = changes(obj1[k], obj2[k]);
if (flags[k].constructor.name == 'Object' && Object.keys(flags[k]).length < 1) {
delete flags[k];
}
break;
default:
flags[k] = true;
}
}
}
});
return flags;
}
// compares two objects and returns whatever properties were overwritten by obj2
export function diff (obj1, obj2) {
let flags = changes(obj1, obj2);
let difference = {};
Object.keys(flags).forEach(k => {
if (typeof obj1[k] == 'undefined' || typeof obj2[k] == 'undefined' || obj1[k] == null || obj2[k] == null || obj1[k].constructor.name !== obj2[k].constructor.name) {
difference[k] = obj2[k];
return;
}
switch (obj1[k].constructor.name) {
case 'Array':
if (obj2[k].constructor.name)
difference[k] = obj2[k].filter(x => !obj1[k].includes(k));
else
difference[k] = obj2[k];
break;
case 'Object':
difference[k] = diff(obj1[k], obj2[k]);
if (Object.keys(difference[k]).length < 1) {
delete difference[k];
}
break;
case 'Number':
case 'Boolean':
case 'Boolean':
case 'String':
default:
difference[k] = obj2[k];
}
});
return difference;
}
Comments