Skip to content

Commit

Permalink
Merge pull request #81 from aidenybai/fix-77
Browse files Browse the repository at this point in the history
Fix #77
  • Loading branch information
aidenybai authored Jul 17, 2021
2 parents b28a804 + 821d150 commit 970ea60
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 34 deletions.
52 changes: 34 additions & 18 deletions src/__test__/patch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,11 @@ const h = (tag: string, props?: VProps, ...children: VNode[]) =>
describe('.patch', () => {
it('should patch element with text as children', () => {
const el = createElement(h('div', { id: 'el' }, 'foo'));
document.body.appendChild(el);

expect(patch(el, h('div', { id: 'el' }, 'bar'))).toEqual(
createElement(h('div', { id: 'el' }, 'bar')),
);
expect(<HTMLElement>document.querySelector('#el')).toEqual(
createElement(h('div', { id: 'el' }, 'bar')),
);
expect(el).toEqual(createElement(h('div', { id: 'el' }, 'bar')));
expect(patch(el, h('div', { id: 'el', class: 'new' }, 'baz'))).toEqual(
createElement(h('div', { id: 'el', class: 'new' }, 'baz')),
);
Expand All @@ -30,14 +27,13 @@ describe('.patch', () => {

it('should patch text', () => {
const el = createElement('foo');
document.body.appendChild(el);
patch(el, 'bar');
expect(el.textContent).toEqual('bar');

expect(patch(el, 'bar', 'foo').nodeValue).toEqual('bar');
});

it('should remove textContent if no children', () => {
const el = createElement('foo');
document.body.appendChild(el);

el.textContent = 'foo';

expect(patch(el, m('div', undefined, undefined, 0)).textContent).toEqual('');
Expand Down Expand Up @@ -114,16 +110,19 @@ describe('.patch', () => {
const children: string[] = [];
const createVNode = () => m('div', undefined, [...children], undefined, [INSERT(0)]);

const prevVNode1 = createVNode();
children.unshift('foo');
patch(el, createVNode());
patch(el, createVNode(), prevVNode1);
expect(el.childNodes.length).toEqual(1);

const prevVNode2 = createVNode();
children.unshift('bar');
patch(el, createVNode());
patch(el, createVNode(), prevVNode2);
expect(el.childNodes.length).toEqual(2);

const prevVNode3 = createVNode();
children.unshift('baz');
patch(el, createVNode());
patch(el, createVNode(), prevVNode3);
expect(el.childNodes.length).toEqual(3);
});

Expand All @@ -133,12 +132,14 @@ describe('.patch', () => {
const children: string[] = ['foo'];
const createVNode = () => m('div', undefined, [...children], undefined, [UPDATE(0)]);

const prevVNode1 = createVNode();
children[0] = 'bar';
patch(el, createVNode());
patch(el, createVNode(), prevVNode1);
expect(el.textContent).toEqual('bar');

const prevVNode2 = createVNode();
children[0] = 'baz';
patch(el, createVNode());
patch(el, createVNode(), prevVNode2);
expect(el.textContent).toEqual('baz');
});

Expand All @@ -147,12 +148,14 @@ describe('.patch', () => {
const children: string[] = [];
const createVNode = () => m('div', undefined, [...children], undefined, [INSERT(0)]);

const prevVNode1 = createVNode();
children.unshift('bar');
patch(el, createVNode());
patch(el, createVNode(), prevVNode1);
expect(el.childNodes.length).toEqual(1);

const prevVNode2 = createVNode();
children.unshift('baz');
patch(el, createVNode());
patch(el, createVNode(), prevVNode2);
expect(el.childNodes.length).toEqual(2);
});

Expand All @@ -164,18 +167,31 @@ describe('.patch', () => {
const children: string[] = ['foo', 'bar', 'baz'];
const createVNode = () => m('div', undefined, [...children], undefined, [DELETE(0)]);

const prevVNode1 = createVNode();
children.splice(0, 1);
patch(el, createVNode());
patch(el, createVNode(), prevVNode1);
expect(el.firstChild!.nodeValue).toEqual('bar');

const prevVNode2 = createVNode();
children.splice(0, 1);
patch(el, createVNode());
patch(el, createVNode(), prevVNode2);
expect(el.firstChild!.nodeValue).toEqual('baz');
});

it('should shortcut if flags are present', () => {
const el = document.createElement('div');
patch(el, m('div', undefined, ['foo'], VFlags.ONLY_TEXT_CHILDREN));
patch(
el,
m('div', undefined, ['foo'], VFlags.ONLY_TEXT_CHILDREN),
m('div', undefined, [], VFlags.ONLY_TEXT_CHILDREN),
);
expect(el.textContent).toEqual('foo');

patch(
el,
m('div', undefined, [], VFlags.NO_CHILDREN),
m('div', undefined, ['foo'], VFlags.NO_CHILDREN),
);
expect(el.childNodes.length).toEqual(0);
});
});
20 changes: 4 additions & 16 deletions src/patch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,9 @@ export const patchChildren = (
};

const replaceElementWithVNode = (el: HTMLElement | Text, newVNode: VNode): HTMLElement | Text => {
if (typeof newVNode === 'string') {
el.textContent = newVNode;
return <Text>el.firstChild;
} else {
const newElement = createElement(newVNode);
el.replaceWith(newElement);
return newElement;
}
const newElement = createElement(newVNode);
el.replaceWith(newElement);
return newElement;
};

/**
Expand Down Expand Up @@ -121,14 +116,7 @@ export const patch = (
(!(<VElement>oldVNode)?.key && !(<VElement>newVNode)?.key) ||
(<VElement>oldVNode)?.key !== (<VElement>newVNode)?.key
) {
if (
(<VElement>oldVNode)?.tag !== (<VElement>newVNode)?.tag &&
!(<VElement>newVNode).children &&
!(<VElement>newVNode).props
) {
// newVNode has no props/children is replaced because it is generally
// faster to create a empty HTMLElement rather than iteratively/recursively
// remove props/children
if ((<VElement>oldVNode)?.tag !== (<VElement>newVNode)?.tag) {
return replaceElementWithVNode(el, newVNode);
}
if (!(el instanceof Text)) {
Expand Down

0 comments on commit 970ea60

Please sign in to comment.