Ensure client data contexts are correctly updated if a parent context is updated

This commit is contained in:
Andrew Kingston 2021-02-09 12:41:21 +00:00
parent 465a240e87
commit a2a805b45b
3 changed files with 52 additions and 37 deletions

View File

@ -11,16 +11,21 @@
// Clone and create new data context for this component tree
const context = getContext("context")
const component = getContext("component")
const newContext = createContextStore($context)
const newContext = createContextStore()
setContext("context", newContext)
let initiated = false
$: providerKey = key || $component.id
// Add data context
$: {
newContext.actions.provideData(providerKey, $context, data)
initiated = true
}
// Instance ID is unique to each instance of a provider
let instanceId
// Add data context
$: data !== undefined && newContext.actions.provideData(providerKey, data)
// Add actions context
$: {
if (instanceId) {
@ -51,4 +56,6 @@
})
</script>
<slot />
{#if initiated}
<slot />
{/if}

View File

@ -1,22 +1,20 @@
import { writable } from "svelte/store"
export const createContextStore = existingContext => {
const store = writable({ ...existingContext })
export const createContextStore = () => {
const store = writable({})
// Adds a data context layer to the tree
const provideData = (providerId, data) => {
if (!providerId) {
return
}
store.update(state => {
state[providerId] = data
const provideData = (providerId, context, data) => {
let newData = { ...context }
if (providerId && data !== undefined) {
newData[providerId] = data
// Keep track of the closest component ID so we can later hydrate a "data" prop.
// This is only required for legacy bindings that used "data" rather than a
// component ID.
state.closestComponentId = providerId
return state
})
newData.closestComponentId = providerId
}
store.set(newData)
}
// Adds an action context layer to the tree
@ -32,7 +30,6 @@ export const createContextStore = existingContext => {
return {
subscribe: store.subscribe,
update: store.update,
actions: { provideData, provideAction },
}
}

View File

@ -1,46 +1,57 @@
<script>
import { onMount, getContext } from "svelte"
const { API, screenStore, routeStore, Provider, styleable } = getContext(
"sdk"
)
const component = getContext("component")
export let table
const {
API,
screenStore,
routeStore,
Provider,
styleable,
ActionTypes,
} = getContext("sdk")
const component = getContext("component")
let headers = []
let row
async function fetchFirstRow() {
const rows = await API.fetchTableData(table)
return Array.isArray(rows) && rows.length ? rows[0] : { tableId: table }
const fetchFirstRow = async tableId => {
const rows = await API.fetchTableData(tableId)
return Array.isArray(rows) && rows.length ? rows[0] : { tableId }
}
async function fetchData() {
if (!table) {
const fetchData = async (rowId, tableId) => {
if (!tableId) {
return
}
const pathParts = window.location.pathname.split("/")
const routeParamId = $routeStore.routeParams.id
// if srcdoc, then we assume this is the builder preview
if ((pathParts.length === 0 || pathParts[0] === "srcdoc") && table) {
row = await fetchFirstRow()
} else if (routeParamId) {
row = await API.fetchRow({ tableId: table, rowId: routeParamId })
if ((pathParts.length === 0 || pathParts[0] === "srcdoc") && tableId) {
row = await fetchFirstRow(tableId)
} else if (rowId) {
row = await API.fetchRow({ tableId, rowId })
} else {
throw new Error("Row ID was not supplied to RowDetail")
}
}
onMount(fetchData)
$: actions = [
{
type: ActionTypes.RefreshDatasource,
callback: () => fetchData($routeStore.routeParams.id, table),
metadata: { datasource: { type: "table", tableId: table } },
},
]
onMount(() => fetchData($routeStore.routeParams.id, table))
</script>
{#if row}
<Provider data={row} {actions}>
<div use:styleable={$component.styles}>
<Provider data={row}>
<slot />
</Provider>
</div>
</Provider>
{/if}