Skip to content

Commit

Permalink
fix: get template method trigger times (#36)
Browse files Browse the repository at this point in the history
* fix: get template method trigger times

* fix: get template method trigger times

* chore: remove useless code

* chore: typo
  • Loading branch information
SoloJiang authored Apr 2, 2022
1 parent 2eac390 commit 3def28c
Show file tree
Hide file tree
Showing 11 changed files with 97 additions and 24 deletions.
23 changes: 12 additions & 11 deletions examples/runtime/src/index.pwc
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
<script>
import { compile, reactive } from 'pwc';
import { html, reactive } from 'pwc';

export default class RuntimeComponent extends HTMLElement {
title = 'pwc';
export default class RuntimeComponent extends HTMLElement {
@reactive
accessor name = 'pwc';

className = 'container'
className = 'container';

handleClick() {
this.title = 'changed title';
}

get template() {
return compile`<div class="${this.className}" @click.capture=${this.handleClick}>${this.title}</div>`;
handleClick = () => {
console.log('click');
this.name = 'changed title';
};

get template() {
return html`<div class="${this.className}" @click.capture=${this.handleClick}>${this.name}${this.name}</div>`;
}
}
}
</script>

<style>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",
"setup": "npm run clean && pnpm install --reporter=append-only --registry=https://registry.npmjs.org/",
"setup": "npm run clean && pnpm install --reporter=append-only --registry=https://registry.npmjs.org/ && npm run build",
"clean": "rm -rf node_modules && rm -rf ./**/*/node_modules && npm run clean:result",
"clean:result": "rm -rf ./packages/*/lib ./packages/*/es ./packages/*/esnext ./packages/*/dist",
"lint": "eslint --cache --ext .js,.jsx,.ts,.tsx ./",
Expand Down
3 changes: 2 additions & 1 deletion packages/pwc/src/decorators/reactive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export function reactive(value, { kind, name }) {
this.setReactiveValue(name, val);
},
init(initialValue) {
this.setReactiveValue(name, initialValue);
this.initReactiveValue(name, initialValue);
return initialValue;
},
};
}
Expand Down
23 changes: 23 additions & 0 deletions packages/pwc/src/elements/__tests__/HTMLElement.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import '../native/HTMLElement';
import { reactive } from '../../decorators/reactive';
import { nextTick } from '../sheduler';
import { compileTemplateInRuntime as html } from '@pwc/compiler';

function getSimpleCustomElement() {
return class CustomElement extends HTMLElement {
Expand Down Expand Up @@ -138,4 +139,26 @@ describe('Render HTMLElement', () => {
expect(element.innerHTML).toEqual('<!--?pwc_p--><div id="reactive-container" class="green">hello?<!--?pwc_t--> - jack!<!--?pwc_t--></div>');
});
});

it('should trigger template method as expected', (done) => {
const mockFn = jest.fn().mockImplementation((text) => {
return html`<div>${text}</div>`;
});
class CustomElement extends HTMLElement {
@reactive
accessor text = 'hello';
get template() {
return mockFn(this.text);
}
}
window.customElements.define('custom-runtime-component', CustomElement);
const element = document.createElement('custom-runtime-component');
document.body.appendChild(element);
expect(mockFn).toBeCalledTimes(1);
element.text = 'world';
nextTick(() => {
expect(mockFn).toBeCalledTimes(2);
done();
});
});
});
18 changes: 18 additions & 0 deletions packages/pwc/src/elements/__tests__/commitAttributes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,4 +122,22 @@ describe('Set element attribute/property/event handler', () => {
// @ts-ignore
expect(customElement.description).toEqual('This is custom element');
});

it('should only add event listener once with component update', () => {
const mockClickHandler = jest.fn();
const div = document.createElement('div');
const attrs = [
{
name: 'onclick',
value: mockClickHandler,
capture: true,
}
];
commitAttributes(div, attrs, true);
div.click();
expect(mockClickHandler).toBeCalledTimes(1);
commitAttributes(div, attrs);
div.click();
expect(mockClickHandler).toBeCalledTimes(2);
});
});
6 changes: 4 additions & 2 deletions packages/pwc/src/elements/commitAttributes.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { isEventName } from '../utils/isEventName';
import type { Attributes } from '../type';

export function commitAttributes(element: Element, attrs: Attributes, isInitial: boolean) {
export function commitAttributes(element: Element, attrs: Attributes, isInitial = false) {
for (const attr of attrs) {
const { name, value } = attr;
if (isEventName(name) && isInitial) {
if (isEventName(name)) {
// Only add event listener at the first render
if (!isInitial) return;
const { capture = false } = attr;
// If capture is true, the event should be triggered when capture stage
element.addEventListener(name.slice(2).toLowerCase(), value, capture);
Expand Down
11 changes: 10 additions & 1 deletion packages/pwc/src/elements/reactiveElementFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ export default (Definition) => {
adoptedCallback() {}

// Extension methods
_getInitialState() {
return this.#initialized;
}

#createTemplate(source: string): Node {
const template = document.createElement('template');

Expand Down Expand Up @@ -90,7 +94,8 @@ export default (Definition) => {
}
}
}
this.#currentTemplate = this.template;
// It will trigger get template method if there use this.template
this.#currentTemplate = [strings, values];
}

requestUpdate(): void {
Expand All @@ -107,5 +112,9 @@ export default (Definition) => {
setReactiveValue(prop: string, val: unknown) {
this.#reactive.setReactiveValue(prop, val);
}

initReactiveValue(prop: string, val: unknown) {
this.#reactive.initReactiveValue(prop, val);
}
};
};
2 changes: 1 addition & 1 deletion packages/pwc/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import './elements';

export * from './decorators';

export { compileTemplateInRuntime as compile } from '@pwc/compiler/compileTemplateInRuntime';
export { compileTemplateInRuntime as html } from '@pwc/compiler/compileTemplateInRuntime';
10 changes: 7 additions & 3 deletions packages/pwc/src/reactivity/__tests__/reactive.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { Reactive } from '../reactive';
class MockElement {
#initialized = false;
isUpdating: boolean = false;
reactive = new Reactive(this);
_getInitialState() {
return this.#initialized;
};
constructor(initialValue) {
this.reactive.setReactiveValue('data', initialValue);
this.isUpdating = false;
this.reactive.initReactiveValue('data', initialValue);
this.#initialized = true;
}
set data(val) {
this.reactive.setReactiveValue('data', val);
Expand Down Expand Up @@ -63,4 +67,4 @@ describe('Create a reactive property', () => {
expect(element.isUpdating).toBe(true);
expect(element.data).toEqual(['Jack']);
});
})
})
17 changes: 14 additions & 3 deletions packages/pwc/src/reactivity/reactive.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getProxyHandler } from './handler';
import { isObject } from '../utils';

interface ReactiveType {
setReactiveValue: (prop: string, val: unknown) => void;
Expand Down Expand Up @@ -30,14 +31,24 @@ export class Reactive implements ReactiveType {
return this.#element[key];
}

setReactiveValue(prop: string, value: unknown) {
initReactiveValue(prop: string, value: unknown) {
const key = Reactive.getKey(prop);
if (typeof value === 'object') {

if (isObject(value)) {
this.#createReactiveProperty(key, value);
} else {
this.#element[key] = value;
}
this.requestUpdate();
}

setReactiveValue(prop: string, value: unknown) {
if (this.#element._getInitialState()) {
this.initReactiveValue(prop, value);
this.requestUpdate();
} else {
// For Object.defineProperty case
this.initReactiveValue(prop, value);
}
}

#createReactiveProperty(key: string, initialValue: any) {
Expand Down
6 changes: 5 additions & 1 deletion packages/pwc/src/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export function isPrimitive(value: unknown) {
return value === null || (typeof value !== 'object' && typeof value !== 'function');
}

export function isObject(value: unknown) {
return typeof value === 'object';
}

export function is(prev, curr): boolean {
// SameValue algorithm
if (prev === curr) {
Expand All @@ -20,4 +24,4 @@ export function is(prev, curr): boolean {
// Step 6.a: NaN == NaN
return prev !== prev && curr !== curr; // eslint-disable-line no-self-compare
}
}
}

0 comments on commit 3def28c

Please sign in to comment.