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 * 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 @@
</script>
{#if constructor}
<svelte:component this={constructor} {...enrichedProps}>
<svelte:component this={constructor} {...componentProps}>
{#if children && children.length}
{#each children as child (getChildKey(child._id))}
<svelte:self definition={child} />

View File

@ -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.