chore: add warning when using non-reactive objects as bindable props

pull/12482/head
Dominic Gannaway 2 years ago
parent bc9907aa1c
commit 66b4d95ee3

@ -0,0 +1,5 @@
---
'svelte': patch
---
chore: add warning when using non-reactive objects as bindable props

@ -1,3 +1,7 @@
## bindable_prop_not_reactive
> The `%name%` prop was passed an object that isn't reactive yet was marked as bindable. The object should be either made reactive using `$state` or should contain properties that have a `set` accessor.
## hydration_attribute_changed ## hydration_attribute_changed
> The `%attribute%` attribute on `%html%` changed its value between server and client renders. The client value, `%value%`, will be ignored in favour of the server value > The `%attribute%` attribute on `%html%` changed its value between server and client renders. The client value, `%value%`, will be ignored in favour of the server value

@ -1,7 +1,16 @@
import { untrack } from './runtime.js'; import { untrack } from './runtime.js';
import { get_descriptor, is_array } from '../shared/utils.js'; import {
array_prototype,
get_descriptor,
get_descriptors,
get_prototype_of,
is_array,
object_prototype
} from '../shared/utils.js';
import * as e from './errors.js'; import * as e from './errors.js';
import * as w from './warnings.js';
import { FILENAME } from '../../constants.js'; import { FILENAME } from '../../constants.js';
import { STATE_SYMBOL } from './constants.js';
/** regex of all html void element names */ /** regex of all html void element names */
const void_element_names = const void_element_names =
@ -67,6 +76,30 @@ export function validate_each_keys(collection, key_fn) {
} }
} }
/**
* @param {any} value
*/
function is_possiblly_reactive_object(value) {
if (STATE_SYMBOL in value) {
return true;
}
const prototype = get_prototype_of(value);
if (prototype === array_prototype || prototype === object_prototype) {
const descriptors = get_descriptors(value);
for (let key in descriptors) {
var prop_value = value[key];
if (
descriptors[key].set ||
(typeof prop_value === 'object' &&
prop_value !== null &&
is_possiblly_reactive_object(prop_value))
)
return true;
}
}
return false;
}
/** /**
* @param {Record<string, any>} $$props * @param {Record<string, any>} $$props
* @param {string[]} bindable * @param {string[]} bindable
@ -87,5 +120,9 @@ export function validate_prop_bindings($$props, bindable, exports, component) {
e.bind_not_bindable(key, component[FILENAME], name); e.bind_not_bindable(key, component[FILENAME], name);
} }
} }
var value = $$props[key];
if (typeof value === 'object' && value !== null && !is_possiblly_reactive_object(value)) {
w.bindable_prop_not_reactive(key);
}
} }
} }

@ -5,6 +5,19 @@ import { DEV } from 'esm-env';
var bold = 'font-weight: bold'; var bold = 'font-weight: bold';
var normal = 'font-weight: normal'; var normal = 'font-weight: normal';
/**
* The `%name%` prop was passed an object that isn't reactive yet was marked as bindable. The object should be either made reactive using `$state` or should contain properties that have a `set` accessor.
* @param {string} name
*/
export function bindable_prop_not_reactive(name) {
if (DEV) {
console.warn(`%c[svelte] bindable_prop_not_reactive\n%cThe \`${name}\` prop was passed an object that isn't reactive yet was marked as bindable. The object should be either made reactive using \`$state\` or should contain properties that have a \`set\` accessor.`, bold, normal);
} else {
// TODO print a link to the documentation
console.warn("bindable_prop_not_reactive");
}
}
/** /**
* The `%attribute%` attribute on `%html%` changed its value between server and client renders. The client value, `%value%`, will be ignored in favour of the server value * The `%attribute%` attribute on `%html%` changed its value between server and client renders. The client value, `%value%`, will be ignored in favour of the server value
* @param {string} attribute * @param {string} attribute

Loading…
Cancel
Save