From ce6d89bc5c408d9458add5157a6ab963669f79b7 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 21 Jan 2021 10:41:30 +0000 Subject: [PATCH] Only update components when their props actually change --- packages/client/src/components/Component.svelte | 17 +++++++++++++++-- packages/client/src/utils/componentProps.js | 16 ++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) 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.