Skip to content

attrs is not reactive when destructured #264

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ferk6a opened this issue Feb 26, 2020 · 12 comments
Closed

attrs is not reactive when destructured #264

ferk6a opened this issue Feb 26, 2020 · 12 comments
Labels
bug Something isn't working

Comments

@ferk6a
Copy link

ferk6a commented Feb 26, 2020

Minimal (non-)working example:

<template>
  <div>
    {{ $attrs['hello-world'] }}
    {{ myComputed }}
  </div>
</template>

<script>
import { defineComponent, computed } from "@vue/composition-api";

export default defineComponent({
  inheritAttrs: false,

  setup(props, { attrs }) {
    const myComputed = computed(() => attrs['hello-world']);

    return {
      myComputed
    }
  }
});
</script>
<template>
    <Example :hello-world="test"/>
   <button @click="change">Change me</button>
</template>

<script>
import { defineComponent, ref } from "@vue/composition-api";

import Example from "@/components/Example.vue";

export default defineComponent({
  components: { Example },

  setup() {
    const test = ref("Hey");

    return {
      change() {
        test.value = "What?";
      },
      test
    }
  }
});
</script>

The value in the template, $attrs changes accordingly, but the computed value doesn't.

@ferk6a ferk6a changed the title attrs is not reactive while root.$attrs works fine attrs is not reactive Feb 26, 2020
@ferk6a
Copy link
Author

ferk6a commented Feb 26, 2020

Ok, solved this by not destructuring the context parameter like this:

<template>
  <div>
    {{ $attrs['hello-world'] }}
    {{ myComputed }}
  </div>
</template>

<script>
import { defineComponent, watch, computed } from "@vue/composition-api";

export default defineComponent({
  inheritAttrs: false,

  setup(props, context) {
    const myComputed = computed(() => {
      return context.attrs['hello-world']
    });

    return {
      myComputed
    }
  }
});
</script>

Lesson learned, but I wish the docs didn't send me into the wrong direction, this is really not correct:


attrs and slots are proxies to the corresponding values on the internal component instance. This ensures they always expose the latest values even after updates so that we can destructure them without worrying accessing a stale reference:

const MyComponent = {
  setup(props, { attrs }) {
    // a function that may get called at a later stage
    function onClick() {
      console.log(attrs.foo) // guaranteed to be the latest reference
    }
  }
}

@ferk6a ferk6a changed the title attrs is not reactive attrs is not reactive when destructured Feb 26, 2020
@LinusBorg
Copy link
Member

LinusBorg commented Feb 28, 2020

Seems to be a bug, rather than a docs issue. But I still need to verify it though as you didn't provide runnable code.

The docs definitely decribe the intended behaviour, and context.attrs indeed a getters/setter"proxy" wrapping vm.$attrs, so it should be reactive:

'attrs',

[...]

proxy(ctx, targetKey, {
get: () => vm[srcKey],
set() {
warn(`Cannot assign to '${targetKey}' because it is a read-only property`, vm);
},
});

edit: at second look, this implementation is indeed buggy in that regard as the destructuring does break the reference to vm.$attrs (noi so much the reactivity per se).

Not too sure this is solvable without involving an Es6 proxy.

@LinusBorg LinusBorg added the bug Something isn't working label Feb 28, 2020
@superbiche
Copy link

I'm having the exact same issue when I'm destructuring props:

// Composable that changes vuex state
async function deleteProviderFile(id) {
    try {
      await $api.delete(`providers/${currentProviderId.value}/files/${id}`)
     // I see the mutation happen in Vuex devtools
      return getProviderField('files').value = getProviderField('files')
        .value
        .filter(f => f.id !== id)
    } catch (e) {
      // TODO error alert
    }
  }

// component destructuring props: removing a picture doesn't update the rendered pictures
setup({ pictures }) {
      const state = reactive({
        featuredPicture: computed(() => pictures[0]),
        firstPictures: computed(() => pictures.slice(1, 5)),
        otherPictures: computed(() => pictures.slice(5)),
      })

      return {
        state,
      }
    },

// without destructuring: the UI updates accordingly
setup(props) {
      const state = reactive({
        featuredPicture: computed(() => props.pictures[0]),
        firstPictures: computed(() => props.pictures.slice(1, 5)),
        otherPictures: computed(() => props.pictures.slice(5)),
      })

      return {
        state,
      }
    },

I'm sorry if there's a more relevant issue, it's actually this one that led me to try without destructuring and fixed my months old bug (which I thought was a brain bug of mine).

@LinusBorg
Copy link
Member

Destructuring props is indeed not supported in that way. You would have to call toRefs() on it.

That's explained in the RFC I think

@superbiche
Copy link

superbiche commented Apr 20, 2020

I was somehow expecting such an answer where I would have badly read either the docs here or the RFC. Very sorry 😅 and thanks a lot @LinusBorg for replying despite my lack of attention!

EDIT: here's the related part of the RFC https://vue-composition-api-rfc.netlify.app/#ref-vs-reactive
So again, my bad!

@antfu antfu closed this as completed Jun 22, 2020
@valgeirb
Copy link

@antfu Can I ask why this was closed? This is still an issue as far as I know.

@posva
Copy link
Member

posva commented Sep 14, 2020

This is expected behavior as pointed out in the last comments ⬆️

@valgeirb
Copy link

The last comments were in relation to props - but losing reactivity when destructuring context is still a bug.

@antfu
Copy link
Member

antfu commented Sep 14, 2020

Sorry, I missed reading the issue so I closed it. However, just like Linus pointed out in this comment #264 (comment), there is no clear way to solve this as destructuring context will break the proxy to vm.$attrs. Maybe I could add it to the limitation of the README (to not destruct context). What do you think?

@valgeirb
Copy link

valgeirb commented Sep 14, 2020

Yeah that might be a good idea if this can't be solved. Not to be confrontational, but are you fine with this package not mirroring the functionality of the Vue 3 composition API?

EDIT: I see now that there are some limitations in the README.

@9-lives
Copy link

9-lives commented Nov 22, 2020

Please don't close this issue because VCA official doc mentioned that attrs and slots are proxies to the corresponding values on the internal component instance .
I just spent 1 hour on figuring it out. Maybe a little tip in readme would be better?

@antfu antfu reopened this Nov 22, 2020
antfu added a commit to antfu/composition-api that referenced this issue Nov 22, 2020
@antfu antfu closed this as completed in 4eecd66 Dec 7, 2020
@m4heshd
Copy link

m4heshd commented Feb 24, 2021

@antfu why doesn't it have the same functionality on Vue 3? It's still not reactive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

8 participants