Gradient
Gradient

Reputation: 145

Vue3 Composition API: Computed value not updating

I am building a project with Nuxt and I need to know the size of the wrapper to adjust the grid setting

(I want a single line, I could still do this in pure CSS probably by hiding the items)

It's my first time using composition API & script setup

<script setup>
const props = defineProps({
    name: String
})
const width = ref(0)
const wrapper = ref(null)
const maxColumns = computed(() => {
    if (width.value < 800) return 3
    if (width.value < 1000) return 4
    return 5
})
onMounted(() => {
    width.value = wrapper.value.clientWidth
    window.onresize = () => {
    width.value = wrapper.value.clientWidth
    console.log(width.value);
    };
})
</script>
<template>
<div class="category-preview" ref="wrapper">
   ...
</div>
</template>

The console log is working properly, resizing the window and refreshing the page will return 3, 4 or 5 depending on the size, but resizing won't trigger the computed value to change

What am I missing ?

Upvotes: 5

Views: 5883

Answers (1)

wittgenstein
wittgenstein

Reputation: 4462

In my test enviroment I had to rename your ref 'width' into something else. After that it did worked for me with a different approach using an event listener for resize events.

You can do something like this:

<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue'

const wrapperWidth = ref(0)
const wrapper = ref(null)

// init component
onMounted(() => {
  getDimensions()
  window.addEventListener('resize', debounce(() => getDimensions(), 250))
})
// remove event listener after destroying the component
onUnmounted(() => {
  window.removeEventListener('resize', debounce)
})

// your computed property
const maxColumns = computed(() => {
  if (wrapperWidth.value < 800) {
    return 3
  } else if (wrapperWidth.value < 1000) {
    return 4
  } else {
    return 5
  }
})

// get template ref dimensions
function getDimensions () {
  const { width } = wrapper.value.getBoundingClientRect()
  wrapperWidth.value = width
}

// wait to call getDimensions()
// it's just a function I have found on the web...
// there is no need to call getDimensions() after every pixel have changed
const debounce = (func, wait) => {
  let timeout
  return function executedFunction (...args) {
    const later = () => {
      timeout = null
      func(...args)
    }
    clearTimeout(timeout)
    timeout = setTimeout(later, wait)
  }
}
</script>

<template>
  <div ref="wrapper">
    {{ maxColumns }} // will change after resize events
  </div>
</template>

Upvotes: 2

Related Questions