Skip to content

Commit

Permalink
change ref behavior to only work for host elements and only fire once
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Kim committed Jan 10, 2024
1 parent a5ad8fd commit 357a564
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 78 deletions.
18 changes: 6 additions & 12 deletions src/crank.ts
Original file line number Diff line number Diff line change
Expand Up @@ -981,23 +981,11 @@ function diffChildren<TNode, TScope, TRoot extends TNode, TResult>(
}
}

const ref = child.ref;
if (isPromiseLike(value)) {
isAsync = true;
if (typeof ref === "function") {
value = value.then((value) => {
ref(renderer.read(value));
return value;
});
}

if (hydrationData !== undefined) {
hydrationBlock = value;
}
} else {
if (typeof ref === "function") {
ref(renderer.read(value));
}
}
} else {
// child is a string or undefined
Expand Down Expand Up @@ -1119,6 +1107,9 @@ function updateRaw<TNode, TScope>(
const props = ret.el.props;
if (!oldProps || oldProps.value !== props.value) {
ret.value = renderer.raw(props.value as any, scope, hydrationData);
if (typeof ret.el.ref === "function") {
ret.el.ref(ret.value);
}
}

return ret.value;
Expand Down Expand Up @@ -1231,6 +1222,9 @@ function commitHost<TNode, TScope>(
if (value == null) {
// This assumes that renderer.create does not return nullish values.
value = ret.value = renderer.create(tag, props, scope);
if (typeof ret.el.ref === "function") {
ret.el.ref(value);
}
}

for (const propName in {...oldProps, ...props}) {
Expand Down
22 changes: 0 additions & 22 deletions test/copy.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {suite} from "uvu";
import * as Assert from "uvu/assert";
import * as Sinon from "sinon";

import {Context, Copy, createElement, Element, Fragment} from "../src/crank.js";
import {renderer} from "../src/dom.js";
Expand Down Expand Up @@ -346,25 +345,4 @@ test("copy async generator siblings with refresh", async () => {
Assert.is(document.body.innerHTML, "<div><span>0</span><span>0</span></div>");
});

test("refs", () => {
renderer.render(<div>Hello</div>, document.body);
const mock = Sinon.fake();
renderer.render(<Copy ref={mock} />, document.body);
Assert.is(mock.lastCall.args[0], document.body.firstChild);
});

test("async refs", async () => {
async function Component() {
await new Promise((resolve) => setTimeout(resolve));
return <span>Hello</span>;
}

const p = renderer.render(<Component />, document.body);
const mock = Sinon.fake();
renderer.render(<Copy ref={mock} />, document.body);
Assert.is(mock.callCount, 0);
await p;
Assert.is(mock.lastCall.args[0], document.body.firstChild);
});

test.run();
69 changes: 25 additions & 44 deletions test/refs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,7 @@ import * as Sinon from "sinon";

const test = suite("refs");

import {
Children,
Context,
createElement,
Element,
Fragment,
Raw,
} from "../src/crank.js";
import {Children, Context, createElement, Element, Raw} from "../src/crank.js";
import {renderer} from "../src/dom.js";

test.after.each(() => {
Expand All @@ -28,42 +21,27 @@ test("basic", () => {
Assert.is(fn.lastCall.args[0], document.body.firstChild);
});

test("child", () => {
test("runs once", () => {
const fn = Sinon.fake();
renderer.render(
<div>
<span ref={fn}>Hello</span>
</div>,
document.body,
);

Assert.is(document.body.innerHTML, "<div><span>Hello</span></div>");
renderer.render(<div ref={fn}>Hello</div>, document.body);
renderer.render(<div ref={fn}>Hello</div>, document.body);
Assert.is(document.body.innerHTML, "<div>Hello</div>");
Assert.is(fn.callCount, 1);
Assert.is(fn.lastCall.args[0], document.body.firstChild!.firstChild);
Assert.is(fn.lastCall.args[0], document.body.firstChild);
});

test("Fragment element", () => {
test("child", () => {
const fn = Sinon.fake();
renderer.render(
<div>
<Fragment ref={fn}>
<span>1</span>
<span>2</span>
<span>3</span>
</Fragment>
<span ref={fn}>Hello</span>
</div>,
document.body,
);

Assert.is(
document.body.innerHTML,
"<div><span>1</span><span>2</span><span>3</span></div>",
);
Assert.is(document.body.innerHTML, "<div><span>Hello</span></div>");
Assert.is(fn.callCount, 1);
Assert.equal(
fn.lastCall.args[0],
Array.from(document.body.firstChild!.childNodes),
);
Assert.is(fn.lastCall.args[0], document.body.firstChild!.firstChild);
});

test("Raw element", () => {
Expand All @@ -80,10 +58,10 @@ test("Raw element", () => {
Assert.ok(refArgs[0] instanceof Node);
});

test("function component", () => {
test("function component ref passing", () => {
const fn = Sinon.fake();
function Component(): Element {
return <span>Hello</span>;
function Component({ref}: {ref: unknown}): Element {
return <span ref={ref}>Hello</span>;
}

renderer.render(
Expand All @@ -98,11 +76,11 @@ test("function component", () => {
Assert.is(fn.lastCall.args[0], document.body.firstChild!.firstChild);
});

test("generator component", () => {
test("generator component ref passing", () => {
const fn = Sinon.fake();
function* Component(): Generator<Element> {
function* Component({ref}: {ref: unknown}): Generator<Element> {
while (true) {
yield <span>Hello</span>;
yield <span ref={ref}>Hello</span>;
}
}

Expand All @@ -118,10 +96,10 @@ test("generator component", () => {
Assert.is(fn.lastCall.args[0], document.body.firstChild!.firstChild);
});

test("async function component", async () => {
test("async function component ref passing", async () => {
const fn = Sinon.fake();
async function Component(): Promise<Element> {
return <span>Hello</span>;
async function Component({ref}: {ref: unknown}): Promise<Element> {
return <span ref={ref}>Hello</span>;
}

await renderer.render(
Expand All @@ -138,9 +116,12 @@ test("async function component", async () => {

test("async generator component", async () => {
const fn = Sinon.fake();
async function* Component(this: Context): AsyncGenerator<Element> {
for await (const _ of this) {
yield <span>Hello</span>;
async function* Component(
this: Context,
{ref}: {ref: unknown},
): AsyncGenerator<Element> {
for await ({ref} of this) {
yield <span ref={ref}>Hello</span>;
}
}

Expand Down

0 comments on commit 357a564

Please sign in to comment.