lit
lit copied to clipboard
[context] `@provide()` in standard decorator mode does not provide initial value
Which package(s) are affected?
Context (@lit/context)
Description
I am creating an HTML-based plugin using Lit. I have a recursive custom element that works like this:
<custom-element>
<custom-element></custom-element>
</custom-element>
To pass data from one <custom-element> to the other down the tree, we need to define both a provider and a consumer on the same element. Using standard decorators, it looks like this:
import {html, css, LitElement} from 'lit';
import {createContext, provide, consume} from '@lit/context';
import {customElement, property} from 'lit/decorators.js';
const foo = createContext<string>(Symbol('foo'))
@customElement('custom-element')
export class CustomElement extends LitElement {
@provide({ context: foo })
accessor provider = 'provided consumer value'
@consume({ context: foo })
accessor consumer = 'default consumer value'
render() {
return html`
${this.consumer || 'undefined consumer value'}
<slot></slot>
`;
}
}
I'd expect to see provided consumer value in the inner <custom-element>, but it displays undefined consumer value instead. With TypeScript decorators, there doesn't seem to be an issue but when using standard decorators, TypeScript throws an error:
Unable to resolve signature of property decorator when called as an expression.
The runtime will invoke the decorator with 3 arguments, but the decorator expects 1-2.
My best guess is that this is due to using accessor, but I don't know enough about accessors to make a firm determination.
Reproduction
TypeScript decorators reproduction (works) Standard decorators reproduction (doesn't work)
Workaround
Aside from switching the entire project to TypeScript decorators, I have not found a workaround.
Is this a regression?
No or unsure. This never worked, or I haven't tried before.
Affected versions
Failing in [email protected] and @lit/[email protected]
Browser/OS/Node environment
Browser: Version 125.0.6422.142 (Official Build) (arm64) OS: macOS Sonoma 14.4.1 Bun version: 1.0.36 Vite version: 5.3.1 TypeScript version: 5.4.5
The lit.dev playground isn't a good test bed for this because it's hardwired for experimental decorators. We have tests of @provide and @consume with standard decorators, if there's a bug we need more coverage, but we see that they compile.
I think the error you're seeing in the playground is that the decorators might not work with experimentalDecorators: true and the accessor keyword.
They are intended to work in this configuration as a way to incrementally migrate toward standard decorators, but we don't seem to have tests for that combination.
Thanks for the quick follow-up @justinfagnani! I'd be a bit surprised if it's TypeScript, as I'm able to use decorators with accessor just fine. Here are the relevant values in my tsconfig.json:
{
"experimentalDecorators": false,
"useDefineForClassFields": true
}
After some investigation, it looks like when using standard decorators, the provider needs to be set manually (e.g., in constructor(), connectedCallback(), etc.). If that doesn't happen, the consumer doesn't receive the initially-provided value. This seems to be happening with custom elements broadly, not just recursive ones, so that part of my previous post is irrelevant. Again, this only seems to be happening with the standard decorators.
Could it be due to the decorator function not considering the initial value?
Seeing the same behavior, and using the same workaround (move init to constructor).
Stepping through provide.ts, I see that init is called before the initializer that sets the ContextProvider.
Stepping through provide.ts, I see that init is called before the initializer that sets the ContextProvider.
Good find @dstoc !
I wonder if the instantiation of the new ContextProvider should move the the init() block and use the value available there as an initialValue option to the ContextProvider constructor.