lit icon indicating copy to clipboard operation
lit copied to clipboard

[labs/context] `@provide` decorator does not work with Babel

Open augustjk opened this issue 3 years ago • 1 comments

See discussion below.

Decorators work fine with TS.

With Babel:

@provide() on its own does not work. @provide() @property() does not work. @property() @provide actually seems to make the context providing work but value is not accessible from the host component.

Discussed in https://github.com/lit/lit/discussions/3804

Originally posted by jrj2211 April 13, 2023 I have an element that is providing some async loaded data from an API. I have children inside the element consuming the context, which is working.

I need my provider to also use the data being provided in the context, but it the property just comes back undefined?

import { userContext } from './contexts/user.js';

export class UserView extends LitElement {
  @property({ attribute: false })
  @provide({ context: userContext })
  user;

  render() {
    return html`
      <user-contact></user-contact>
      <div>${this.user?.name}</div>
    `;
  }

  async connectedCallback() {
    super.connectedCallback();
    const user = await App.client.service('users').findOne(this.id});
    this.user = user;
    console.log('provider', 'user', user) // not undefined
    console.log('provider', 'user', this.user) // is undefined?
  }
}

Logging out the loaded user, its undefined. The child user-contact that @consumes the userContext, gets the data.

Is this correct? The documentation show adding @property along with @provide, so why isn't the value available in the provider element via this.user?

augustjk avatar Apr 14 '23 03:04 augustjk

Here are some findings after investigating.

The @lit-labs/context decorators make use if decorateProperty() https://github.com/lit/lit/blob/a1f8c345cc978be06c6416edd1228fc5471c53d8/packages/reactive-element/src/decorators/base.ts#L63

When this is used without providing a descriptor and only finisher as it is done with @provide(), in Babel mode this ends up creating the property on the instance with placement: "own" rather than the prototype, so the setter defined in the finisher ends up getting masked. It happened to be that if you do @property() @provide(), the @property() decorator was overriding this behavior and not creating an instance property, allowing the setter to work, but since the @provide() never sets a getter, its value is still not accessible.

When doing @provide() @property(), the finisher defined for @provide() never gets the correct property key as it gets the Symbol() from the @property() decorator. The code that's meant to handle this https://github.com/lit/lit/blob/a1f8c345cc978be06c6416edd1228fc5471c53d8/packages/reactive-element/src/decorators/base.ts#L89-L94 does not actually work as it seems originalKey is not carried over in Babel's implementation for chaining decorators.

I think the single @provide() use for babel is fixable by either making decorateProperty() always set the placement to prototype instead of own, and make sure the @provide() decorator accounts for data descriptors that have writable and value, or have the @provide() implementation just provide the getter/setter with the descriptor option for decorateProperty(). The latter has implications with chaining decorators or when the user provides getter/setter of their own.

Considering that Babel's removing support for the "2018-09" version (https://github.com/babel/babel/pull/12712), I wonder if it's worth our time to even consider fixing this.

cc: @justinfagnani

augustjk avatar Apr 19 '23 00:04 augustjk

Not planning on supporting the nonstandard 2018 Babel transform with context

rictic avatar Jul 06 '23 17:07 rictic