Skip to content

[fix] bind:this works during onMount callback in manually-created component #6920

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Dec 13, 2021
23 changes: 14 additions & 9 deletions src/runtime/internal/scheduler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { run_all } from './utils';
import { set_current_component } from './lifecycle';
import { run_all, Queue } from './utils';
import { get_current_component, set_current_component } from './lifecycle';

export const dirty_components = [];
export const dirty_components = new Queue<any>();
export const intros = { enabled: false };

export const binding_callbacks = [];
Expand Down Expand Up @@ -31,17 +31,21 @@ export function add_flush_callback(fn) {
flush_callbacks.push(fn);
}

let flushing = false;
const seen_callbacks = new Set();
export function flush() {
if (flushing) return;
flushing = true;

let current_component = null;
try {
current_component = get_current_component();
} catch {
// no current component, so leave it as null
}

do {
// first, call beforeUpdate functions
// and update components
for (let i = 0; i < dirty_components.length; i += 1) {
const component = dirty_components[i];
while (dirty_components.length) {
const component = dirty_components.shift();
set_current_component(component);
update(component.$$);
}
Expand All @@ -68,12 +72,13 @@ export function flush() {
render_callbacks.length = 0;
} while (dirty_components.length);

set_current_component(current_component);

while (flush_callbacks.length) {
flush_callbacks.pop()();
}

update_scheduled = false;
flushing = false;
seen_callbacks.clear();
}

Expand Down
34 changes: 34 additions & 0 deletions src/runtime/internal/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,40 @@ export function is_empty(obj) {
return Object.keys(obj).length === 0;
}

export class Queue<T> {
forward: T[];
reverse: T[];

constructor() {
this.forward = [];
this.reverse = [];
}
push(value: T) {
return this.forward.push(value);
}
shift() {
if (this.reverse.length === 0) {
while (this.forward.length) {
this.reverse.push(this.forward.pop());
}
}
return this.reverse.pop();
}
get length() {
return this.forward.length + this.reverse.length;
}
set length(len: number) {
if (len === 0) {
this.forward.length = 0;
this.reverse.length = 0;
} else {
while (this.length > len) {
this.shift();
}
}
}
}

export function validate_store(store, name) {
if (store != null && typeof store.subscribe !== 'function') {
throw new Error(`'${name}' is not a store with a 'subscribe' method`);
Expand Down
15 changes: 15 additions & 0 deletions test/runtime/samples/component-binding-onMount/Mount.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<script>
import { onMount } from 'svelte';

let element;
let bound = false;
onMount(() => {
if (element) bound = true;
});

</script>

<div bind:this={element}></div>
<p>
Bound? {bound}
</p>
11 changes: 11 additions & 0 deletions test/runtime/samples/component-binding-onMount/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default {
async test({ assert, target }) {
assert.htmlEqual(target.innerHTML, `
<div id="target"><div></div>
<p>
Bound? true
</p>
</div>
`);
}
};
13 changes: 13 additions & 0 deletions test/runtime/samples/component-binding-onMount/main.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<script>
import Mount from './Mount.svelte';
import { onMount } from 'svelte';

onMount(() => {
const component = new Mount({
target: document.querySelector('#target'),
props: {},
});
});
</script>

<div id="target" />