Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 26 additions & 15 deletions src/core/vdom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ export interface WidgetMeta {
nodeMap: Map<string | number, HTMLElement>;
destroyMap: Map<string, () => void>;
deferRefs: number;
diffMap: Map<string, (current: any, next: any) => void>;
customDiffProperties: Set<string>;
customDiffMap: Map<string, Map<string, (current: any, next: any) => void>>;
}

export interface WidgetData {
Expand Down Expand Up @@ -436,19 +437,18 @@ export function tsx(tag: any, properties = {}, ...children: any[]): DNode {
}
}

function propertiesDiff(current: any, next: any, invalidator: () => void) {
function propertiesDiff(current: any, next: any, invalidator: () => void, ignoreProperties: string[]) {
const propertyNames = [...Object.keys(current), ...Object.keys(next)];
let diffedProperties = [];
for (let i = 0; i < propertyNames.length; i++) {
if (diffedProperties.indexOf(propertyNames[i]) > -1) {
if (ignoreProperties.indexOf(propertyNames[i]) > -1) {
continue;
}
const result = auto(current[propertyNames[i]], next[propertyNames[i]]);
if (result.changed) {
invalidator();
break;
}
diffedProperties.push(propertyNames[i]);
ignoreProperties.push(propertyNames[i]);
}
}

Expand Down Expand Up @@ -719,8 +719,10 @@ function destroyHandles(destroyMap: Map<string, () => void>) {
}

function runDiffs(meta: WidgetMeta, current: any, next: any) {
if (meta.diffMap.size) {
meta.diffMap.forEach((diff) => diff({ ...current }, { ...next }));
if (meta.customDiffMap.size) {
meta.customDiffMap.forEach((diffMap) => {
diffMap.forEach((diff) => diff({ ...current }, { ...next }));
});
}
}

Expand Down Expand Up @@ -751,14 +753,17 @@ export const node = factory(({ id }) => {
};
});

export const diffProperties = factory(({ id }) => {
return (diff: (current: any, next: any) => void) => {
export const diffProperty = factory(({ id }) => {
return (propertyName: string, diff: (current: any, next: any) => void) => {
const [widgetId] = id.split('-');
const widgetMeta = widgetMetaMap.get(widgetId);
if (widgetMeta) {
if (!widgetMeta.diffMap.has(id)) {
widgetMeta.diffMap.set(id, diff);
const propertyDiffMap = widgetMeta.customDiffMap.get(id) || new Map();
if (!propertyDiffMap.has(propertyName)) {
propertyDiffMap.set(propertyName, diff);
widgetMeta.customDiffProperties.add(propertyName);
}
widgetMeta.customDiffMap.set(id, propertyDiffMap);
}
};
});
Expand Down Expand Up @@ -1730,7 +1735,8 @@ export function renderer(renderer: () => RenderResult): Renderer {
nodeMap: new Map(),
destroyMap: new Map(),
deferRefs: 0,
diffMap: new Map()
customDiffMap: new Map(),
customDiffProperties: new Set()
};
widgetMetaMap.set(next.id, widgetMeta);
widgetMeta.middleware = (Constructor as any).middlewares
Expand Down Expand Up @@ -1823,9 +1829,14 @@ export function renderer(renderer: () => RenderResult): Renderer {
widgetMeta.properties = next.properties;
runDiffs(widgetMeta, current.properties, next.properties);
if (!widgetMeta.dirty) {
propertiesDiff(current.properties, next.properties, () => {
widgetMeta.dirty = true;
});
propertiesDiff(
current.properties,
next.properties,
() => {
widgetMeta.dirty = true;
},
[...widgetMeta.customDiffProperties.values()]
);
}
if (widgetMeta.dirty) {
next.childrenWrappers = undefined;
Expand Down
37 changes: 32 additions & 5 deletions tests/core/unit/vdom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import sendEvent from '../support/sendEvent';
import {
create,
renderer,
diffProperties,
diffProperty,
defer,
destroy,
getRegistry,
Expand Down Expand Up @@ -3404,12 +3404,12 @@ jsdomDescribe('vdom', () => {
});
});

describe('diffProperties', () => {
it('Should call diff properties before rendering', () => {
const createWidget = create({ diffProperties, invalidator });
describe('diffProperty', () => {
it('Should call registered custom diff property function before rendering', () => {
const createWidget = create({ diffProperty, invalidator });
let counter = 0;
const Foo = createWidget(({ middleware }) => {
middleware.diffProperties((current: any, properties: any) => {
middleware.diffProperty('key', (current: any, properties: any) => {
assert.deepEqual(current, { key: 'foo' });
assert.deepEqual(properties, { key: 'foo' });
middleware.invalidator();
Expand All @@ -3434,6 +3434,33 @@ jsdomDescribe('vdom', () => {
resolvers.resolve();
assert.strictEqual(root.outerHTML, '<div><div><button></button><div>1</div></div></div>');
});

it('Should skip properties from the standard diff that have a custom diff registered', () => {
const createWidget = create({ diffProperty, invalidator }).properties<any>();
const Foo = createWidget(({ middleware, properties }) => {
middleware.diffProperty('text', (current: any, properties: any) => {});
return v('div', [properties.text]);
});
let text = 'first';
const App = createWidget(({ middleware }) => {
return v('div', [
v('button', {
onclick: () => {
text = 'second';
middleware.invalidator();
}
}),
Foo({ key: 'foo', text })
]);
});
const r = renderer(() => App({}));
const root = document.createElement('div');
r.mount({ domNode: root });
assert.strictEqual(root.outerHTML, '<div><div><button></button><div>first</div></div></div>');
sendEvent(root.childNodes[0].childNodes[0] as HTMLButtonElement, 'click');
resolvers.resolve();
assert.strictEqual(root.outerHTML, '<div><div><button></button><div>first</div></div></div>');
});
});
});
});
Expand Down