Vue: Inject/Provide is not reactive

Created on 7 Nov 2017  路  12Comments  路  Source: vuejs/vue

Version

2.5.3

Reproduction link

https://codesandbox.io/s/7o2qp4l3z6

Steps to reproduce

Parent Component

    provide() {
      let reactive = {};

      Object.defineProperties(reactive, {
        challenge: {
          get: () => this.challenge
        }
      });

      return reactive;
    },

Child Component

inject: ["challenge"]

What is expected?

The challenge property should change when its parent this.challenge changes on the proxy

What is actually happening?

Not changing at all


The link provided is just an example how to reimplement what i'm really doing but that is the same code that i use

any ideas?

Most helpful comment

I'd like to point out two possible solutions to this problem:

  1. There is a plugin/mixin now by @LinusBorg to deal with this: https://github.com/LinusBorg/vue-reactive-provide
  2. Instead of providing the actual computed property, you could provide a method that is to be used as a getter by the children that inject it to then get the property when needed:
// parent:
{
  provide() {
    return {
      $computedProperty: () => this.computedProperty
    }
  },

  computed: {
    computedProperty() {
      // Returns the result of some potentially reactive computation
    }
  }
}

// children:
{
  inject: [
    '$computedProperty'
  ],

  computed: {
    computedProperty() {
      return this.$computedProperty()
    }
聽聽}
}

All 12 comments

Unfortunately, that's by design. you can hack around it using an object an referring to its children, but it's not recommended because it could break in future versions of Vue

I'd like to point out two possible solutions to this problem:

  1. There is a plugin/mixin now by @LinusBorg to deal with this: https://github.com/LinusBorg/vue-reactive-provide
  2. Instead of providing the actual computed property, you could provide a method that is to be used as a getter by the children that inject it to then get the property when needed:
// parent:
{
  provide() {
    return {
      $computedProperty: () => this.computedProperty
    }
  },

  computed: {
    computedProperty() {
      // Returns the result of some potentially reactive computation
    }
  }
}

// children:
{
  inject: [
    '$computedProperty'
  ],

  computed: {
    computedProperty() {
      return this.$computedProperty()
    }
聽聽}
}

That second trick is great, never thought of that!

@LinusBorg a function used as a tunnel / wormhole through the non-reactive JS space-continuum :)

@lehni
Just a small mistake, this is not accessible within the provide object you need to declare it like this for it to work

provide() {
    return {
        $computedProperty: () => this.computedProperty,
    };
  },

@Tofandel hmmm but you have the same code there as I do? And I'm using this in production, sure works for me...

@Tofandel never mind, I just noticed now that @posva edited my code to include your fix before I saw your post. I also have this version in my own use, not sure why I posted the object version above initially :)

@lehni Haha okay I thought you edited it so I was confused :p

This must be added to the recipes or guides.

I have discovered new way:

// parent:
{
  provide() {
    const _this = this;

    return {
      get computedProperty () {
         return _this.computedProperty;
      }
    }
  },

  computed: {
    computedProperty() {
      // Returns the result of some potentially reactive computation
    }
  }
}

// children:
{
  inject: [
    'computedProperty'
  ],
}

You don't need computed method in children anymore.

I discovered a flaw in @lehni 's trick today. Wrapping the object in a function triggers an error when the providing object gets unmounted I think, as if the injection briefly couldn't be resolved anymore:

[Vue warn]: Injection "$computedProperty" not found

This error doesn't happen when a plain object is passed.
I could circumvent it by providing a wrapped-wrapped (!) default value in the child component:

// children:
{
  inject: [
    '$computedProperty': {
      // you need to provide a default object that doesn't trigger errors
      // in your component when used
      default: () => () => {}
    }
  ],
  computed: {
    computedProperty() {
      return this.$computedProperty()
    }
  }
}

It feels dirty overall. We should probably avoid passing functions.
As a reminder, the Vue docs advise against inject/provide anyway, and just use props, even if it's more verbose.

@cihad I haven't been able to make your trick work, it's not reactive for me. And I needed two different names to avoid a loop: get computedProperty () { return _this.$computedProperty }

It seems like the only way to get it to work is by providing a function, and then in the component it is injected in you need to create a computed prop and call the function like the example above.

Has there been any progress on opinions of how to tackle this? I dont want to prop drill or use state management. Provide/Inject seems like the best solution, but feels weird we need to hack around the cleanest solution.

Thoughts?

Was this page helpful?
0 / 5 - 0 ratings