0

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.

1 Answer 1

2

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>
Sign up to request clarification or add additional context in comments.

1 Comment

This can be done in a simpler way by using an array containing updated ids instead of updatedEntries set, but otherwise that's the way to go

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.