diff --git a/packages/client/src/components/Component.svelte b/packages/client/src/components/Component.svelte index 322d083f94..ffa6b38c43 100644 --- a/packages/client/src/components/Component.svelte +++ b/packages/client/src/components/Component.svelte @@ -3,10 +3,11 @@ import { writable } from "svelte/store" import * as ComponentLibrary from "@budibase/standard-components" import Router from "./Router.svelte" - import { enrichProps } from "../utils/componentProps" + import { enrichProps, propsAreSame } from "../utils/componentProps" import { bindingStore, builderStore } from "../store" export let definition = {} + let componentProps = {} // Get contexts const dataContext = getContext("data") @@ -21,6 +22,7 @@ $: children = definition._children $: id = definition._id $: enrichedProps = enrichProps(definition, $dataContext, $bindingStore) + $: updateProps(enrichedProps) $: styles = definition._styles // Allow component selection in the builder preview if we're previewing a @@ -31,6 +33,17 @@ // Update component context $: componentStore.set({ id, styles: { ...styles, id, allowSelection } }) + // Updates the component props. + // Most props are deeply compared so that svelte will only trigger reactive + // statements on props that have actually changed. + const updateProps = props => { + Object.keys(props).forEach(key => { + if (!propsAreSame(props[key], componentProps[key])) { + componentProps[key] = props[key] + } + }) + } + // Gets the component constructor for the specified component const getComponentConstructor = component => { const split = component?.split("/") @@ -51,7 +64,7 @@ {#if constructor} - + {#if children && children.length} {#each children as child (getChildKey(child._id))} diff --git a/packages/client/src/utils/componentProps.js b/packages/client/src/utils/componentProps.js index be65ad2bfe..c5dffe4336 100644 --- a/packages/client/src/utils/componentProps.js +++ b/packages/client/src/utils/componentProps.js @@ -1,6 +1,22 @@ import { enrichDataBindings } from "./enrichDataBinding" import { enrichButtonActions } from "./buttonActions" +/** + * Deeply compares 2 props using JSON.stringify. + * Does not consider functions, as currently only button actions have a function + * prop and it's cheaper to just always re-render buttons than it is to deeply + * compare them. + */ +export const propsAreSame = (a, b) => { + if (a === b) { + return true + } + if (typeof a === "function" || typeof b === "function") { + return false + } + return JSON.stringify(a) === JSON.stringify(b) +} + /** * Enriches component props. * Data bindings are enriched, and button actions are enriched.