Skip to content
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

Reactive controllers with LWC #2592

Open
pmdartus opened this issue Dec 6, 2021 · 9 comments
Open

Reactive controllers with LWC #2592

pmdartus opened this issue Dec 6, 2021 · 9 comments

Comments

@pmdartus
Copy link
Member

pmdartus commented Dec 6, 2021

Reactive controller is a pattern introduced with lit@2 enabling code reuse and composition between components. It has been shown that the ReactiveController interface can be adapted to other frameworks. There are also some side discussions to make this a community standard.

In essence, reactive controllers are quite similar to the LWC @wire decorator. Both are companion objects to the component and both have access to certain life-cycle hooks.

After looking at the ReactiveController interface, the main blocker on LWC is the lack of API to force a component to rerender. I have mixed feelings about exposing such API on the LightningElement as it we want developers to rely on the props reactivity to trigger rerender. This is not the only missing API required to implement the reactive controller protocol, but I think it is the most controversial one.

@nolanlawson
Copy link
Contributor

If we allowed Reactive Controllers to opt-in to the reactivity tracking, would we need to implement requestUpdate? Based on the clock-controller.ts example in the Lit docs, if controllers somehow had reactivity tracking, we could just make requestUpdate a no-op:

this.value = new Date() // re-render happens here
this.host.requestUpdate() // no-op

@jfu-sfdc
Copy link

jfu-sfdc commented Dec 6, 2021

If we allowed Reactive Controllers to opt-in to the reactivity tracking, would we need to implement requestUpdate? Based on the clock-controller.ts example in the Lit docs, if controllers somehow had reactivity tracking, we could just make requestUpdate a no-op:

this.value = new Date() // re-render happens here
this.host.requestUpdate() // no-op

this.value = new Date() happens inside the controller. I'm not sure how you can trigger a re-render there without an api call?

@nolanlawson
Copy link
Contributor

I'm not sure how you can trigger a re-render there without an api call?

The same way we enable deep tracking? In fact I wonder if this would already work today:

// component.js
import { LightningElement, track } from 'lwc'
export default class extends LightningElement {
  @track // deep track the controller
  clockController = new ClockController(this)
}

@jfu-sfdc
Copy link

jfu-sfdc commented Dec 8, 2021

@nolanlawson @pmdartus This is super useful for our AriaObserver use case, is there anything we can do to help making it happen in LWC?

@abdulsattar
Copy link
Contributor

This is similar to the Plugin System (Custom Directives) that we talked about.

@pmdartus
Copy link
Member Author

pmdartus commented Dec 9, 2021

The same way we enable deep tracking? In fact I wonder if this would already work today:

This approach (#2592 (comment)) will not work, as the track decorator only wraps arrays and objects with the null or Object.prototype prototype. In this case, the clock controller field will have ClockController prototype, which will never be observed. This restriction is in place to avoid all sort of issues related to object identity.

class Foo {
	#val = 1;

	printVal() {
  	console.log(this.#val);
  }
}

const raw = new Foo();
raw.printVal();  // val


const proxified = new Proxy(new Foo(), {});
proxified.printVal(); // 🚨 Error: Cannot read private member #val from an object whose class did not declare it

@caridy
Copy link
Collaborator

caridy commented Dec 11, 2021

certainly, this is inline with the conversation about the plugins system for LWC. Now, initially, I was proposing that the plugins will operate over the DOM element directly rather than the component instance. In Lit, they are the same thing, in LWC, they are different things but we want them to be the same thing at some point. So, I will be OK with exploring this idea with the component instead of the element, assuming that the component will give you almost all the APIs that you will ever need.

As for reactivity, I don't think those controllers should be reactive in any way, but if they need to be, then we should rely on the track decorator for those fields declared on the controller itself. In theory the track decorator should be universal, it is not at the moment.

Finally, the requestUpdate is certainly the hard cookie to swallow here because of the amount of effort and thinking that we have put onto avoiding this in LWC all together. @pmdartus can we do some research about how different frameworks are currently allowing or not such level of control? and what kind of guardrails they put in place?

@jfu-sfdc
Copy link

jfu-sfdc commented Apr 4, 2022

@caridy @pmdartus @nolanlawson It's been a few months since the last discussion. Has there been any progress on the plugin system?

@caridy
Copy link
Collaborator

caridy commented Apr 5, 2022

@caridy @pmdartus @nolanlawson It's been a few months since the last discussion. Has there been any progress on the plugin system?

No, no that I'm aware of.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants