add updating guard to binding callback (#5126)

pull/5170/head
Tan Li Hau 4 years ago committed by GitHub
parent 5b80874cd4
commit ec0f79c5af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,6 +2,7 @@
## Unreleased
* Prevent duplicate invalidation with certain two-way component bindings ([#3180](https://github.com/sveltejs/svelte/issues/3180), [#5117](https://github.com/sveltejs/svelte/issues/5117), [#5144](https://github.com/sveltejs/svelte/issues/5144))
* Fix reactivity when passing `$$props` to a `<slot>` ([#3364](https://github.com/sveltejs/svelte/issues/3364))
* Fix unneeded invalidation of `$$props` and `$$restProps` ([#4993](https://github.com/sveltejs/svelte/issues/4993), [#5118](https://github.com/sveltejs/svelte/issues/5118))

@ -86,7 +86,6 @@ export default function dom(
const set = (uses_props || uses_rest || writable_props.length > 0 || component.slots.size > 0)
? x`
${$$props} => {
${(uses_props || uses_rest) && b`if (@is_empty(${$$props})) return;`}
${uses_props && renderer.invalidate('$$props', x`$$props = @assign(@assign({}, $$props), @exclude_internal_props($$new_props))`)}
${uses_rest && !uses_props && x`$$props = @assign(@assign({}, $$props), @exclude_internal_props($$new_props))`}
${uses_rest && renderer.invalidate('$$restProps', x`$$restProps = ${compute_rest}`)}
@ -421,7 +420,7 @@ export default function dom(
${component.partly_hoisted}
${set && b`$$self.$set = ${set};`}
${set && b`$$self.$$set = ${set};`}
${capture_state && b`$$self.$capture_state = ${capture_state};`}

@ -1,6 +1,6 @@
import { add_render_callback, flush, schedule_update, dirty_components } from './scheduler';
import { current_component, set_current_component } from './lifecycle';
import { blank_object, is_function, run, run_all, noop } from './utils';
import { blank_object, is_empty, is_function, run, run_all, noop } from './utils';
import { children, detach } from './dom';
import { transition_in } from './transitions';
@ -33,6 +33,7 @@ interface T$$ {
context: Map<any, any>;
on_mount: any[];
on_destroy: any[];
skip_bound: boolean;
}
export function bind(component, name, callback) {
@ -120,7 +121,8 @@ export function init(component, options, instance, create_fragment, not_equal, p
// everything else
callbacks: blank_object(),
dirty
dirty,
skip_bound: false
};
let ready = false;
@ -129,7 +131,7 @@ export function init(component, options, instance, create_fragment, not_equal, p
? instance(component, prop_values, (i, ret, ...rest) => {
const value = rest.length ? rest[0] : ret;
if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) {
if ($$.bound[i]) $$.bound[i](value);
if (!$$.skip_bound && $$.bound[i]) $$.bound[i](value);
if (ready) make_dirty(component, i);
}
return ret;
@ -166,6 +168,7 @@ export let SvelteElement;
if (typeof HTMLElement === 'function') {
SvelteElement = class extends HTMLElement {
$$: T$$;
$$set?: ($$props: any) => void;
constructor() {
super();
this.attachShadow({ mode: 'open' });
@ -199,14 +202,19 @@ if (typeof HTMLElement === 'function') {
};
}
$set() {
// overridden by instance, if it has props
$set($$props) {
if (this.$$set && !is_empty($$props)) {
this.$$.skip_bound = true;
this.$$set($$props);
this.$$.skip_bound = false;
}
}
};
}
export class SvelteComponent {
$$: T$$;
$$set?: ($$props: any) => void;
$destroy() {
destroy_component(this, 1);
@ -223,7 +231,11 @@ export class SvelteComponent {
};
}
$set() {
// overridden by instance, if it has props
$set($$props) {
if (this.$$set && !is_empty($$props)) {
this.$$.skip_bound = true;
this.$$set($$props);
this.$$.skip_bound = false;
}
}
}

@ -55,7 +55,7 @@ function instance($$self, $$props, $$invalidate) {
let { bar } = $$props;
const foo_function = () => handleFoo(bar);
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("bar" in $$props) $$invalidate(0, bar = $$props.bar);
};

@ -52,7 +52,7 @@ function instance($$self, $$props, $$invalidate) {
$$invalidate(0, open);
}
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("open" in $$props) $$invalidate(0, open = $$props.open);
};

@ -46,7 +46,7 @@ function instance($$self, $$props, $$invalidate) {
$$invalidate(1, h);
}
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("w" in $$props) $$invalidate(0, w = $$props.w);
if ("h" in $$props) $$invalidate(1, h = $$props.h);
};

@ -68,7 +68,7 @@ function instance($$self, $$props, $$invalidate) {
$$invalidate(0, files);
}
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("files" in $$props) $$invalidate(0, files = $$props.files);
};

@ -118,7 +118,7 @@ function instance($$self, $$props, $$invalidate) {
let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("prop" in $$props) $$subscribe_prop($$invalidate(0, prop = $$props.prop));
if ("alias" in $$props) $$invalidate(1, realName = $$props.alias);
};

@ -48,7 +48,7 @@ function create_fragment(ctx) {
function instance($$self, $$props, $$invalidate) {
let { foo = 42 } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("foo" in $$props) $$invalidate(0, foo = $$props.foo);
};

@ -12,7 +12,7 @@ function instance($$self, $$props, $$invalidate) {
return x * 3;
}
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("x" in $$props) $$invalidate(0, x = $$props.x);
};

@ -47,7 +47,7 @@ function create_fragment(ctx) {
function instance($$self, $$props, $$invalidate) {
let { bar } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("bar" in $$props) $$invalidate(0, bar = $$props.bar);
};

@ -79,7 +79,7 @@ function instance($$self, $$props, $$invalidate) {
let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("name" in $$props) $$invalidate(0, name = $$props.name);
};

@ -183,7 +183,7 @@ function instance($$self, $$props, $$invalidate) {
let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("things" in $$props) $$invalidate(0, things = $$props.things);
if ("foo" in $$props) $$invalidate(1, foo = $$props.foo);
if ("bar" in $$props) $$invalidate(2, bar = $$props.bar);

@ -175,7 +175,7 @@ function instance($$self, $$props, $$invalidate) {
let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("things" in $$props) $$invalidate(0, things = $$props.things);
if ("foo" in $$props) $$invalidate(1, foo = $$props.foo);
};

@ -104,7 +104,7 @@ function create_fragment(ctx) {
function instance($$self, $$props, $$invalidate) {
let { createElement } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("createElement" in $$props) $$invalidate(0, createElement = $$props.createElement);
};

@ -10,7 +10,7 @@ function instance($$self, $$props, $$invalidate) {
alert(JSON.stringify(data()));
});
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("foo" in $$props) $$invalidate(0, foo = $$props.foo);
};

@ -76,7 +76,7 @@ function instance($$self, $$props, $$invalidate) {
let { $$slots = {}, $$scope } = $$props;
validate_slots("Component", $$slots, []);
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("foo" in $$props) $$invalidate(0, foo = $$props.foo);
};

@ -106,7 +106,7 @@ function instance($$self, $$props, $$invalidate) {
let { d } = $$props;
let { e } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("a" in $$props) $$invalidate(0, a = $$props.a);
if ("b" in $$props) $$invalidate(1, b = $$props.b);
if ("c" in $$props) $$invalidate(2, c = $$props.c);

@ -152,7 +152,7 @@ function instance($$self, $$props, $$invalidate) {
let { time } = $$props;
let { foo } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("comments" in $$props) $$invalidate(0, comments = $$props.comments);
if ("elapsed" in $$props) $$invalidate(1, elapsed = $$props.elapsed);
if ("time" in $$props) $$invalidate(2, time = $$props.time);

@ -128,7 +128,7 @@ function foo(node, animation, params) {
function instance($$self, $$props, $$invalidate) {
let { things } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("things" in $$props) $$invalidate(0, things = $$props.things);
};

@ -97,7 +97,7 @@ function create_fragment(ctx) {
function instance($$self, $$props, $$invalidate) {
let { things } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("things" in $$props) $$invalidate(0, things = $$props.things);
};

@ -88,7 +88,7 @@ function create_fragment(ctx) {
function instance($$self, $$props, $$invalidate) {
let { foo } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("foo" in $$props) $$invalidate(0, foo = $$props.foo);
};

@ -66,7 +66,7 @@ function create_fragment(ctx) {
function instance($$self, $$props, $$invalidate) {
let { foo } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("foo" in $$props) $$invalidate(0, foo = $$props.foo);
};

@ -44,7 +44,7 @@ function instance($$self, $$props, $$invalidate) {
let { x } = $$props;
let { y } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("color" in $$props) $$invalidate(0, color = $$props.color);
if ("x" in $$props) $$invalidate(1, x = $$props.x);
if ("y" in $$props) $$invalidate(2, y = $$props.y);

@ -37,7 +37,7 @@ function create_fragment(ctx) {
function instance($$self, $$props, $$invalidate) {
let { data } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("data" in $$props) $$invalidate(0, data = $$props.data);
};

@ -37,7 +37,7 @@ function create_fragment(ctx) {
function instance($$self, $$props, $$invalidate) {
let { color } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("color" in $$props) $$invalidate(0, color = $$props.color);
};

@ -54,7 +54,7 @@ function instance($$self, $$props, $$invalidate) {
let { key } = $$props;
let { value } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("style" in $$props) $$invalidate(0, style = $$props.style);
if ("key" in $$props) $$invalidate(1, key = $$props.key);
if ("value" in $$props) $$invalidate(2, value = $$props.value);

@ -49,7 +49,7 @@ function instance($$self, $$props, $$invalidate) {
$$invalidate(0, files);
}
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("files" in $$props) $$invalidate(0, files = $$props.files);
};

@ -60,7 +60,7 @@ function instance($$self, $$props, $$invalidate) {
$$invalidate(0, value);
}
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("value" in $$props) $$invalidate(0, value = $$props.value);
};

@ -53,7 +53,7 @@ function instance($$self, $$props, $$invalidate) {
$$invalidate(0, foo);
}
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("foo" in $$props) $$invalidate(0, foo = $$props.foo);
};

@ -173,7 +173,7 @@ function instance($$self, $$props, $$invalidate) {
$$invalidate(10, ended);
}
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("buffered" in $$props) $$invalidate(0, buffered = $$props.buffered);
if ("seekable" in $$props) $$invalidate(1, seekable = $$props.seekable);
if ("played" in $$props) $$invalidate(2, played = $$props.played);

@ -167,7 +167,7 @@ function instance($$self, $$props, $$invalidate) {
let { f } = $$props;
let Component;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("a" in $$props) $$invalidate(0, a = $$props.a);
if ("b" in $$props) $$invalidate(1, b = $$props.b);
if ("c" in $$props) $$invalidate(2, c = $$props.c);

@ -6,7 +6,7 @@ function instance($$self, $$props, $$invalidate) {
let a;
let b;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("x" in $$props) $$invalidate(0, x = $$props.x);
};

@ -5,7 +5,7 @@ function instance($$self, $$props, $$invalidate) {
let { a = 1 } = $$props;
let { b = 2 } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("a" in $$props) $$invalidate(0, a = $$props.a);
if ("b" in $$props) $$invalidate(1, b = $$props.b);
};

@ -50,7 +50,7 @@ function create_fragment(ctx) {
function instance($$self, $$props, $$invalidate) {
let { current } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("current" in $$props) $$invalidate(0, current = $$props.current);
};

@ -67,7 +67,7 @@ function instance($$self, $$props, $$invalidate) {
let { url } = $$props;
let { slug } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("url" in $$props) $$invalidate(0, url = $$props.url);
if ("slug" in $$props) $$invalidate(1, slug = $$props.slug);
};

@ -22,7 +22,7 @@ function create_fragment(ctx) {
function instance($$self, $$props, $$invalidate) {
let { custom } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("custom" in $$props) $$invalidate(0, custom = $$props.custom);
};

@ -124,7 +124,7 @@ function instance($$self, $$props, $$invalidate) {
let { x } = $$props;
let { y } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("x" in $$props) $$invalidate(0, x = $$props.x);
if ("y" in $$props) $$invalidate(1, y = $$props.y);
};

@ -102,7 +102,7 @@ function create_fragment(ctx) {
function instance($$self, $$props, $$invalidate) {
let { num = 1 } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("num" in $$props) $$invalidate(0, num = $$props.num);
};

@ -243,7 +243,7 @@ function instance($$self, $$props, $$invalidate) {
let { d } = $$props;
let { e } = $$props;
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("a" in $$props) $$invalidate(0, a = $$props.a);
if ("b" in $$props) $$invalidate(1, b = $$props.b);
if ("c" in $$props) $$invalidate(2, c = $$props.c);

@ -93,7 +93,7 @@ function instance($$self, $$props, $$invalidate) {
$$invalidate(3, offsetWidth);
}
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("currentTime" in $$props) $$invalidate(0, currentTime = $$props.currentTime);
if ("videoHeight" in $$props) $$invalidate(1, videoHeight = $$props.videoHeight);
if ("videoWidth" in $$props) $$invalidate(2, videoWidth = $$props.videoWidth);

@ -78,7 +78,7 @@ function instance($$self, $$props, $$invalidate) {
$$invalidate(0, y = window.pageYOffset)
}
$$self.$set = $$props => {
$$self.$$set = $$props => {
if ("y" in $$props) $$invalidate(0, y = $$props.y);
};

@ -0,0 +1,5 @@
<script>
export let value = '';
</script>
<input bind:value />

@ -0,0 +1,61 @@
export default {
html: `
<input />
<input />
<div></div>
`,
async test({ assert, component, target, window }) {
let count = 0;
component.callback = () => {
count++;
};
const [input1, input2] = target.querySelectorAll("input");
input1.value = "1";
await input1.dispatchEvent(new window.Event("input"));
assert.htmlEqual(
target.innerHTML,
`
<input />
<input />
<div>1</div>
`
);
assert.equal(input1.value, "1");
assert.equal(input2.value, "1");
assert.equal(count, 1);
input2.value = "123";
await input2.dispatchEvent(new window.Event("input"));
assert.htmlEqual(
target.innerHTML,
`
<input />
<input />
<div>123</div>
`
);
assert.equal(input1.value, "123");
assert.equal(input2.value, "123");
assert.equal(count, 2);
input1.value = "456";
await input1.dispatchEvent(new window.Event("input"));
assert.htmlEqual(
target.innerHTML,
`
<input />
<input />
<div>456</div>
`
);
assert.equal(input1.value, "456");
assert.equal(input2.value, "456");
assert.equal(count, 3);
},
};

@ -0,0 +1,18 @@
<script>
import { writable } from 'svelte/store';
import Input from './Input.svelte';
let value = writable({ value: '' });
export let callback = () => {};
value.subscribe(() => {
callback();
})
</script>
<input bind:value={$value.value} />
<Input bind:value={$value.value}/>
<div>{$value.value}</div>
Loading…
Cancel
Save