Skip to content

Commit

Permalink
Add queued rerendering of changed views
Browse files Browse the repository at this point in the history
  • Loading branch information
stwa committed May 23, 2019
1 parent ceb7c0b commit 84a6923
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 3 deletions.
16 changes: 13 additions & 3 deletions src/dom.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { signalFn } from "./signal";
import { queue } from "./internal/queue";

/**
* The dom module helps to connect view functions with signal circuits. A view
Expand All @@ -12,6 +13,8 @@ import { signalFn } from "./signal";
* @module dom
*/

export const renderQueue = queue();

/**
* Mounts the result of `viewFn` as a replacement of `root`.
*
Expand Down Expand Up @@ -59,14 +62,21 @@ import { signalFn } from "./signal";
*/
export function mount(root, viewFn, patchFn, cleanupFn) {
let s = signalFn(viewFn);
let dirty = true;

let render = next => {
root = patchFn(root, next);
let render = () => {
if (dirty) {
root = patchFn(root, s.value());
dirty = false;
}
};

render(s.value());

let disconnect = s.connect((signal, prev, next) => render(next));
let disconnect = s.connect(() => {
dirty = true;
renderQueue.enqueue(render);
});

return () => {
disconnect();
Expand Down
43 changes: 43 additions & 0 deletions src/dom.test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
import { mount } from "./dom";
import { signal } from "./signal";
import { renderQueue } from "./dom";

/* global global */

let ticker = (function() {
let fns = [];
return {
dispatch(fn) {
fns = [...fns, fn];
},
advance() {
let fn = fns.pop();
if (fn) fn();
},
size() {
return fns.length;
},
};
})();

beforeEach(() => {
global.console.warn = jest.fn();
document.body.innerHTML = '<div id="my-view"></div>';
renderQueue.tickFn(ticker.dispatch);
});

describe("mount", () => {
Expand Down Expand Up @@ -37,12 +55,35 @@ describe("mount", () => {
mount(document.querySelector("#my-view"), viewFn, patchFn);

s.reset("bar");
ticker.advance();

expect(patches).toHaveLength(2);
let [prev, next] = patches[1];
expect(prev).toBe("foo");
expect(next).toBe("bar");
});
it("renders only when view is dirty", () => {
let patches = [];
let patchFn = (prev, next) => {
patches = [...patches, [prev, next]];
return next;
};
let s = signal("foo");
let viewFn = () => s.value();

mount(document.querySelector("#my-view"), viewFn, patchFn);

s.reset("bar");
s.reset("baz");

expect(ticker.size()).toBe(1);
ticker.advance();

expect(patches).toHaveLength(2);
let [prev, next] = patches[1];
expect(prev).toBe("foo");
expect(next).toBe("baz");
});
});

describe("unmount", () => {
Expand All @@ -59,11 +100,13 @@ describe("unmount", () => {
expect(patched).toBe(1);

s.reset("bar");
ticker.advance();
expect(patched).toBe(2);

unmount();

s.reset("baz");
ticker.advance();
expect(patched).toBe(2);
});
it("calls the cleanup function and returns its result", () => {
Expand Down
28 changes: 28 additions & 0 deletions src/internal/queue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
export function queue(nextTick = window.requestAnimationFrame) {
let queue = [];
let scheduled = false;

return {
enqueue(fn) {
queue = [...queue, fn];
this.schedule();
},
flush() {
scheduled = false;

let fns = [...queue];
queue = [];

fns.forEach(fn => fn());
},
schedule() {
if (!scheduled) {
scheduled = true;
nextTick(this.flush);
}
},
tickFn(fn) {
nextTick = fn;
},
};
}

0 comments on commit 84a6923

Please sign in to comment.