Skip to content

Commit 17096e6

Browse files
committed
update bind:this references when setting to null - fixes #2034
1 parent 220515b commit 17096e6

File tree

7 files changed

+61
-22
lines changed

7 files changed

+61
-22
lines changed

src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ export default class InlineComponentWrapper extends Wrapper {
324324

325325
component.partly_hoisted.push(body);
326326

327-
return `@add_binding_callback(() => @bind(${this.var}, '${binding.name}', ${name}));`;
327+
return `@binding_callbacks.push(() => @bind(${this.var}, '${binding.name}', ${name}));`;
328328
});
329329

330330
const munged_handlers = this.node.handlers.map(handler => {

src/compiler/compile/render_dom/wrappers/shared/bind_this.ts

+23-8
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export default function bind_this(component: Component, block: Block, binding: B
1515

1616
let lhs;
1717
let object;
18+
let body;
1819

1920
if (binding.is_contextual && binding.expression.node.type === 'Identifier') {
2021
// bind:x={y} — we can't just do `y = x`, we need to
@@ -23,10 +24,19 @@ export default function bind_this(component: Component, block: Block, binding: B
2324
const { snippet } = block.bindings.get(name);
2425
lhs = snippet;
2526

26-
// TODO we need to invalidate... something
27+
body = `${lhs} = $$value`; // TODO we need to invalidate... something
2728
} else {
2829
object = flatten_reference(binding.expression.node).name;
2930
lhs = component.source.slice(binding.expression.node.start, binding.expression.node.end).trim();
31+
32+
body = binding.expression.node.type === 'Identifier'
33+
? deindent`
34+
${component.invalidate(object, `${lhs} = $$value`)};
35+
`
36+
: deindent`
37+
${lhs} = $$value;
38+
${component.invalidate(object)};
39+
`
3040
}
3141

3242
const contextual_dependencies = Array.from(binding.expression.contextual_dependencies);
@@ -35,8 +45,9 @@ export default function bind_this(component: Component, block: Block, binding: B
3545
component.partly_hoisted.push(deindent`
3646
function ${fn}(${['$$value', ...contextual_dependencies].join(', ')}) {
3747
if (${lhs} === $$value) return;
38-
${lhs} = $$value;
39-
${object && component.invalidate(object)}
48+
@binding_callbacks[$$value ? 'unshift' : 'push'](() => {
49+
${body}
50+
});
4051
}
4152
`);
4253

@@ -56,25 +67,29 @@ export default function bind_this(component: Component, block: Block, binding: B
5667

5768
const condition = Array.from(contextual_dependencies).map(name => `${name} !== ctx.${name}`).join(' || ');
5869

70+
// we push unassign and unshift assign so that references are
71+
// nulled out before they're created, to avoid glitches
72+
// with shifting indices
5973
block.builders.update.add_line(deindent`
6074
if (${condition}) {
6175
${unassign}();
6276
${args.map(a => `${a} = ctx.${a}`).join(', ')};
63-
@add_binding_callback(${assign});
77+
${assign}();
6478
}`
6579
);
6680

6781
block.builders.destroy.add_line(`${unassign}();`);
68-
return `@add_binding_callback(${assign});`;
82+
return `${assign}();`;
6983
}
7084

7185
component.partly_hoisted.push(deindent`
7286
function ${fn}($$value) {
73-
${lhs} = $$value;
74-
${object && component.invalidate(object)}
87+
@binding_callbacks[$$value ? 'unshift' : 'push'](() => {
88+
${body}
89+
});
7590
}
7691
`);
7792

7893
block.builders.destroy.add_line(`ctx.${fn}(null);`);
79-
return `@add_binding_callback(() => ctx.${fn}(${variable}));`;
94+
return `ctx.${fn}(${variable});`;
8095
}

src/runtime/internal/scheduler.ts

+4-7
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@ import { set_current_component } from './lifecycle';
44
export const dirty_components = [];
55
export const intros = { enabled: false };
66

7-
const resolved_promise = Promise.resolve();
8-
let update_scheduled = false;
9-
const binding_callbacks = [];
7+
export const binding_callbacks = [];
108
const render_callbacks = [];
119
const flush_callbacks = [];
1210

11+
const resolved_promise = Promise.resolve();
12+
let update_scheduled = false;
13+
1314
export function schedule_update() {
1415
if (!update_scheduled) {
1516
update_scheduled = true;
@@ -22,10 +23,6 @@ export function tick() {
2223
return resolved_promise;
2324
}
2425

25-
export function add_binding_callback(fn) {
26-
binding_callbacks.push(fn);
27-
}
28-
2926
export function add_render_callback(fn) {
3027
render_callbacks.push(fn);
3128
}

test/runtime/index.js

+3-5
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,10 @@ describe("runtime", () => {
9898
};
9999
set_now(() => raf.time);
100100
set_raf(cb => {
101-
let called = false;
102101
raf.callback = () => {
103-
if (!called) {
104-
called = true;
105-
cb();
106-
}
102+
raf.callback = null;
103+
cb();
104+
flush();
107105
};
108106
});
109107

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export default {
2+
skip_if_ssr: true,
3+
4+
html: `
5+
<div>The text is hello</div>
6+
<h1>hello</h1>
7+
`,
8+
9+
test({ assert, component, target }) {
10+
component.visible = false;
11+
assert.htmlEqual(target.innerHTML, `
12+
<div>The text is missing</div>
13+
`);
14+
15+
component.visible = true;
16+
assert.htmlEqual(target.innerHTML, `
17+
<div>The text is hello</div>
18+
<h1>hello</h1>
19+
`);
20+
}
21+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
export let visible = true;
3+
let h1;
4+
</script>
5+
6+
<div>The text is {h1 ? h1.textContent : 'missing'}</div>
7+
{#if visible}
8+
<h1 bind:this={h1}>hello</h1>
9+
{/if}

test/runtime/samples/transition-js-if-block-outro-timeout/_config.js

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export default {
88

99
raf.tick(200);
1010
assert.equal(window.getComputedStyle(div).opacity, 0.5);
11-
component.blabla = false;
1211

1312
raf.tick(400);
1413
assert.equal(window.getComputedStyle(div).opacity, 0);

0 commit comments

Comments
 (0)