Reputation: 8423
Consider a reactive object (or array) that is dynamically populated with data:
import { reactive, computed } from 'vue'
const entries = reactive({}) // global state beyond Template level
const docs = [
{ id: 1, name: 'foo', value: 'bar' },
{ id: 2, name: 'bar', value: 'moo' }
]
Object.entries(docs).forEach(doc => {
entries[doc.id] = doc
})
export const useEntries = () => {
const getAll = computed(() => Object.values(entries))
const update = (id, newValue) => {
entries[id].value = newValue
}
const changes = () => { /* ??? */ }
return { getAll, update, changes }
}
For performance reason, I want a Templates to watch only updated entries and not iterate the entire list, if the update is triggered within another Template.
Template A:
<script setup>
import { useEntries } from '/path/to/useEntries'
const { update } = useEntries()
</script>
<template>
<button @click="update(1, 'moo)">Make moo</button>
</template>
Template B:
<script setup>
import { useEntries } from '/path/to/useEntries'
import { watch } from 'vue'
const { changes } = useEntries()
watch(changes, (entries) => {
// process only updated entries here
})
</script>
Note, in this example I update only one entry, but it also might be 50 at once in a list of hundreds. I'd like to avoid a second list to manage.
Upvotes: 0
Views: 37
Reputation: 525
you can manage "delta" object that holds changes you store entry’s id in the changes object then add changes function that returns only entries that have been updated
import { reactive, computed } from 'vue'
const entries = reactive({})
const updatedEntries = reactive(new Set())
const docs = [
{ id: 1, name: 'foo', value: 'bar' },
{ id: 2, name: 'bar', value: 'moo' }
]
docs.forEach(doc => {
entries[doc.id] = doc
})
export const useEntries = () => {
const getAll = computed(() => Object.values(entries))
const update = (id, newValue) => {
entries[id].value = newValue
updatedEntries.add(id)
}
const changes = computed(() => {
const changed = {}
updatedEntries.forEach(id => {
changed[id] = entries[id]
})
return changed
})
return { getAll, update, changes }
}
Template A
<template>
<button @click="update(1, 'moo')">Make moo</button>
</template>
<script setup>
import { useEntries } from '/path/to/useEntries'
const { update } = useEntries()
</script>
Template B
<template>
<div v-for="entry in changedEntries" :key="entry.id">
{{ entry.name }}: {{ entry.value }}
</div>
</template>
<script setup>
import { useEntries } from '/path/to/useEntries'
import { watch, ref } from 'vue'
const { changes } = useEntries()
const changedEntries = ref({})
watch(changes, (newChanges) => {
changedEntries.value = newChanges
})
</script>
Upvotes: 2