Only update components when their props actually change

This commit is contained in:
Andrew Kingston 2021-01-21 10:41:30 +00:00
parent 3d77c1f38f
commit ce6d89bc5c
2 changed files with 31 additions and 2 deletions

View File

@ -3,10 +3,11 @@
import { writable } from "svelte/store" import { writable } from "svelte/store"
import * as ComponentLibrary from "@budibase/standard-components" import * as ComponentLibrary from "@budibase/standard-components"
import Router from "./Router.svelte" import Router from "./Router.svelte"
import { enrichProps } from "../utils/componentProps" import { enrichProps, propsAreSame } from "../utils/componentProps"
import { bindingStore, builderStore } from "../store" import { bindingStore, builderStore } from "../store"
export let definition = {} export let definition = {}
let componentProps = {}
// Get contexts // Get contexts
const dataContext = getContext("data") const dataContext = getContext("data")
@ -21,6 +22,7 @@
$: children = definition._children $: children = definition._children
$: id = definition._id $: id = definition._id
$: enrichedProps = enrichProps(definition, $dataContext, $bindingStore) $: enrichedProps = enrichProps(definition, $dataContext, $bindingStore)
$: updateProps(enrichedProps)
$: styles = definition._styles $: styles = definition._styles
// Allow component selection in the builder preview if we're previewing a // Allow component selection in the builder preview if we're previewing a
@ -31,6 +33,17 @@
// Update component context // Update component context
$: componentStore.set({ id, styles: { ...styles, id, allowSelection } }) $: 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 // Gets the component constructor for the specified component
const getComponentConstructor = component => { const getComponentConstructor = component => {
const split = component?.split("/") const split = component?.split("/")
@ -51,7 +64,7 @@
</script> </script>
{#if constructor} {#if constructor}
<svelte:component this={constructor} {...enrichedProps}> <svelte:component this={constructor} {...componentProps}>
{#if children && children.length} {#if children && children.length}
{#each children as child (getChildKey(child._id))} {#each children as child (getChildKey(child._id))}
<svelte:self definition={child} /> <svelte:self definition={child} />

View File

@ -1,6 +1,22 @@
import { enrichDataBindings } from "./enrichDataBinding" import { enrichDataBindings } from "./enrichDataBinding"
import { enrichButtonActions } from "./buttonActions" 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. * Enriches component props.
* Data bindings are enriched, and button actions are enriched. * Data bindings are enriched, and button actions are enriched.