Skip to content

Commit

Permalink
Fix performance of Angular rerender
Browse files Browse the repository at this point in the history
  • Loading branch information
jonrimmer committed Mar 14, 2019
1 parent 8cfc2c2 commit df6bf95
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 21 deletions.
18 changes: 14 additions & 4 deletions addons/knobs/src/registerKnobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,28 @@ function setPaneKnobs(timestamp = +new Date()) {
channel.emit(SET, { knobs: knobStore.getAll(), timestamp });
}

// Increased performance by reducing the number of times a component is rendered during knob changes
const debouncedOnKnobChanged = debounce(() => {
const resetAndForceUpdate = () => {
knobStore.markAllUnused();
forceReRender();
}, COMPONENT_FORCE_RENDER_DEBOUNCE_DELAY_MS);
};

// Increase performance by reducing how frequently the story is recreated during knob changes
const debouncedResetAndForceUpdate = debounce(
resetAndForceUpdate,
COMPONENT_FORCE_RENDER_DEBOUNCE_DELAY_MS
);

function knobChanged(change) {
const { name } = change;
const { value } = change; // Update the related knob and it's value.
const knobOptions = knobStore.get(name);
knobOptions.value = value;
debouncedOnKnobChanged();

if (!manager.options.disableDebounce) {
debouncedResetAndForceUpdate();
} else {
resetAndForceUpdate();
}
}

function knobClicked(clicked) {
Expand Down
1 change: 1 addition & 0 deletions app/angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"@angular/platform-browser-dynamic": ">=6.0.0",
"autoprefixer": "^8.1.0",
"babel-loader": "^7.0.0 || ^8.0.0",
"rxjs": "^6.0.0",
"zone.js": "^0.8.29"
},
"publishConfig": {
Expand Down
33 changes: 23 additions & 10 deletions app/angular/src/client/preview/angular/components/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
} from '@angular/core';
import { STORY } from '../app.token';
import { NgStory, ICollection } from '../types';
import { Observable, Subscription } from 'rxjs';
import { first } from 'rxjs/operators';

@Component({
selector: 'storybook-dynamic-app-root',
Expand All @@ -24,22 +26,33 @@ import { NgStory, ICollection } from '../types';
export class AppComponent implements OnInit, OnDestroy {
@ViewChild('target', { read: ViewContainerRef })
target: ViewContainerRef;
constructor(private cfr: ComponentFactoryResolver, @Inject(STORY) private data: NgStory) {}

subscription: Subscription;

constructor(
private cfr: ComponentFactoryResolver,
@Inject(STORY) private data: Observable<NgStory>
) {}

ngOnInit(): void {
this.putInMyHtml();
}
this.data.pipe(first()).subscribe((data: NgStory) => {
this.target.clear();
const compFactory = this.cfr.resolveComponentFactory(data.component);
const ref = this.target.createComponent(compFactory);
const instance = ref.instance;

ngOnDestroy(): void {
this.target.clear();
this.subscription = this.data.subscribe(newData => {
this.setProps(instance, newData);
ref.changeDetectorRef.detectChanges();
});
});
}

private putInMyHtml(): void {
ngOnDestroy(): void {
this.target.clear();
const compFactory = this.cfr.resolveComponentFactory(this.data.component);
const instance = this.target.createComponent(compFactory).instance;

this.setProps(instance, this.data);
if (this.subscription) {
this.subscription.unsubscribe();
}
}

/**
Expand Down
15 changes: 12 additions & 3 deletions app/angular/src/client/preview/angular/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './components/app.component';
import { STORY } from './app.token';
import { NgModuleMetadata, IStoryFn, NgStory } from './types';
import { ReplaySubject } from 'rxjs';

let platform: any = null;
let promises: Array<Promise<NgModuleRef<any>>> = [];
Expand All @@ -14,17 +15,21 @@ const componentClass = class DynamicComponent {};

type DynamicComponentType = typeof componentClass;

const storyData = new ReplaySubject(1);

const getModule = (
declarations: Array<Type<any> | any[]>,
entryComponents: Array<Type<any> | any[]>,
bootstrap: Array<Type<any> | any[]>,
data: NgStory,
moduleMetadata: NgModuleMetadata
) => {
storyData.next(data);

const moduleMeta = {
declarations: [...declarations, ...(moduleMetadata.declarations || [])],
imports: [BrowserModule, FormsModule, ...(moduleMetadata.imports || [])],
providers: [{ provide: STORY, useValue: { ...data } }, ...(moduleMetadata.providers || [])],
providers: [{ provide: STORY, useValue: storyData }, ...(moduleMetadata.providers || [])],
entryComponents: [...entryComponents, ...(moduleMetadata.entryComponents || [])],
schemas: [...(moduleMetadata.schemas || [])],
bootstrap: [...bootstrap],
Expand Down Expand Up @@ -86,6 +91,10 @@ const draw = (newModule: DynamicComponentType): void => {
}
};

export const renderNgApp = (storyFn: IStoryFn) => {
draw(initModule(storyFn));
export const renderNgApp = (storyFn: IStoryFn, forced: boolean) => {
if (!forced) {
draw(initModule(storyFn));
} else {
storyData.next(storyFn());
}
};
4 changes: 2 additions & 2 deletions app/angular/src/client/preview/render.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { renderNgApp } from './angular/helpers';

export default function render({ storyFn, showMain }) {
export default function render({ storyFn, showMain, forceRender }) {
showMain();
renderNgApp(storyFn);
renderNgApp(storyFn, forceRender);
}
2 changes: 1 addition & 1 deletion examples/angular-cli/src/stories/addon-knobs.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { SimpleKnobsComponent } from './knobs.component';
import { AllKnobsComponent } from './all-knobs.component';

storiesOf('Addon|Knobs', module)
.addDecorator(withKnobs)
.addDecorator(withKnobs({ disableDebounce: true }))
.add('Simple', () => {
const name = text('name', 'John Doe');
const age = number('age', 0);
Expand Down
2 changes: 2 additions & 0 deletions examples/angular-cli/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"target": "es5",
"typeRoots": ["../../node_modules/@types", "node_modules/@types"],
"lib": ["es2017", "dom"]
Expand Down
1 change: 0 additions & 1 deletion tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"comment-format": [true, "check-space"],
"curly": true,
"forin": true,
"import-blacklist": [true, "rxjs"],
"interface-over-type-literal": true,
"label-position": true,
"member-access": false,
Expand Down

0 comments on commit df6bf95

Please sign in to comment.