[fix] bind:this during onMount in manually-created component (#6920)

pull/7015/head
Robin Munn 3 years ago committed by GitHub
parent 683c39adb7
commit 5a8851436f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,5 @@
import { run_all } from './utils';
import { set_current_component } from './lifecycle';
import { current_component, set_current_component } from './lifecycle';
export const dirty_components = [];
export const intros = { enabled: false };
@ -31,23 +31,42 @@ export function add_flush_callback(fn) {
flush_callbacks.push(fn);
}
let flushing = false;
// flush() calls callbacks in this order:
// 1. All beforeUpdate callbacks, in order: parents before children
// 2. All bind:this callbacks, in reverse order: children before parents.
// 3. All afterUpdate callbacks, in order: parents before children. EXCEPT
// for afterUpdates called during the initial onMount, which are called in
// reverse order: children before parents.
// Since callbacks might update component values, which could trigger another
// call to flush(), the following steps guard against this:
// 1. During beforeUpdate, any updated components will be added to the
// dirty_components array and will cause a reentrant call to flush(). Because
// the flush index is kept outside the function, the reentrant call will pick
// up where the earlier call left off and go through all dirty components. The
// current_component value is saved and restored so that the reentrant call will
// not interfere with the "parent" flush() call.
// 2. bind:this callbacks cannot trigger new flush() calls.
// 3. During afterUpdate, any updated components will NOT have their afterUpdate
// callback called a second time; the seen_callbacks set, outside the flush()
// function, guarantees this behavior.
const seen_callbacks = new Set();
let flushidx = 0; // Do *not* move this inside the flush() function
export function flush() {
if (flushing) return;
flushing = true;
const saved_component = current_component;
do {
// first, call beforeUpdate functions
// and update components
for (let i = 0; i < dirty_components.length; i += 1) {
const component = dirty_components[i];
while (flushidx < dirty_components.length) {
const component = dirty_components[flushidx];
flushidx++;
set_current_component(component);
update(component.$$);
}
set_current_component(null);
dirty_components.length = 0;
flushidx = 0;
while (binding_callbacks.length) binding_callbacks.pop()();
@ -73,8 +92,8 @@ export function flush() {
}
update_scheduled = false;
flushing = false;
seen_callbacks.clear();
set_current_component(saved_component);
}
function update($$) {

@ -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>

@ -0,0 +1,11 @@
export default {
async test({ assert, target }) {
assert.htmlEqual(target.innerHTML, `
<div id="target"><div></div>
<p>
Bound? true
</p>
</div>
`);
}
};

@ -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" />
Loading…
Cancel
Save