-
Notifications
You must be signed in to change notification settings - Fork 962
[labs] Reactive controller adapters for other frameworks #1682
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
Comments
I had thought that Svelte didn't have a way for an external object to hook the lifecycle or force an update, but @RyanCarniato Svelte usage seems like it could be fairly idiomatic. The controller is modeled as a store and with auto-bindings you can refer to the controller state in the component and template: import {makeSvelteController} from './controllers.js';
import {MouseController} from './mouse-controller.js';
export const mouseController = makeSvelteController((host) => new MouseController(host)) <script>
import {mouseController} from './svelte-mouse-controller.js'
const mouse = mouseController();
</script>
<pre>
x: {$mouse.pos.x}
y: {$mouse.pos.y}
</pre> |
Proof-of-concept AngularControllerHost here: https://stackblitz.com/edit/angular-ivy-uuiqpj?file=src%2Fapp%2Fangular-controller-host.ts |
Love to see all of this 🎉 Nevertheless, will you be targeting both Renderer2 and/only Ivy lifecycles for Angular? |
For
|
I'm not sure. What are those? :) |
For |
@userquin how do you call |
Try this: <script lang="ts">
import { defineComponent, getCurrentInstance } from 'vue'
export default defineComponent({
setup() {
const vm = getCurrentInstance()
const update = () => {
vm?.ctx?.$forceUpdate()
}
return { update }
},
})
</script>
<template>
<div>
...
</div>
<button @click="update">
Refresh
</button>
</template> |
or with script setup (similar to <script setup lang="ts">
import { getCurrentInstance } from 'vue'
const vm = getCurrentInstance()
const update = () => {
vm?.update()
}
</script>
<template>
<div>
...
</div>
<button @click="update">
Refresh
</button>
</template> |
@justinfagnani the problem is that with If we include some For example: <script lang="ts">
import { defineComponent, getCurrentInstance, ref } from 'vue'
export default defineComponent({
setup() {
const counter = ref(0)
const vm = getCurrentInstance()
const update = () => {
counter.value++
//vm?.update()
}
return { update, counter }
},
})
</script>
<template>
<div>
<button @click="update">
Refresh
</button>
<div :class="`c-${counter}`">
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
</div>
</div>
</template> With no |
to be sure the update is triggering, use this, you will see <script lang="ts">
import { defineComponent, getCurrentInstance/*, ref */, onUpdated } from 'vue'
export default defineComponent({
setup() {
// const counter = ref(0)
const vm = getCurrentInstance()
const update = () => {
// counter.value++
vm?.update()
}
onUpdated(() => {
console.log('UPDATED')
})
return { update/*, counter */ }
},
})
</script>
<template>
<div>
<button @click="update">
Refresh
</button>
<!-- <div :class="`c-${counter}`">-->
<div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
<div>hey</div>
</div>
</div>
</template> |
Thanks @userquin - |
@justinfagnani on You can split |
I don't think I'll prototype Vue 2, since Vue 3 is the latest. Someone else could though. |
FYI: hook names on your table are for |
Angular would use |
Click on the Prototype link in the table footer... |
About Angular, you were uncertain about why you don't need to do anything for the The solution would be something like |
So - Angular is moving from Renderer2 to Ivy and really focusing on dropping a reliance on Zone.js - that being said you will have to choose what you want to support/if you want to support the old Renderer2 at all. Personally I would recommend supporting both as the most likely users of LitElement are those incrementally updating a large dated Application. The following is a great library to study with regards to Angular Component (Template) rendering & Lifecycles. Additionally in most contemporary cases Ivy + a ChangeDetectionStrategy is used along with ChangeDetectorRef is used to manually call a markForCheck() similar to requestUpdate() With regards to Renderer2 there are some helpful callbacks to know about the state of the current render cycle, often this is helpful when one is using a (RendererFactory)[https://angular.io/api/core/RendererFactory2] to gain granular control/integration over the process. Helpful Renderer2 DOM Algo Insights These API's/Processes In conjunction to the current lifecycle should provide enough tools to integrate ReactiveControllers no prob. LMK if I can help further. -T |
Zoneless Angular applications is a long-term effort. With async-await patched in NgZone by Angular, it might not have a very high priority. |
@justinfagnani you can use |
|
@bennypowers I have a POC for FAST reactive controllers. It makes use of a private API.
I adopted the examples from Lit's example docs and theyall appear to work. https://github.com/ParamagicDev/reactive-fast-element/tree/main/src/examples I know its not the official "behavior" way and ive been told it bypasses a lot of FAST's performance enhancements for templates, but as far as I can tell, it works 🤷 Its available both as an extendable base class, or as a mixin. |
This is true, but long term concern is that the private method you're using will become inaccessible, so if they decide to add a It's worth noting that Reactive controllers can do a lot more than re-rendering, so there's still value in having an adapter. It's unfortunate that FAST's position on this is to build a FAST behavior for each RC you want to use (source). Surely it would be more efficient to have a single adapter — or to build support for RCs into the library. Maybe we can continue to push for |
Is there an open issue for this on FAST? |
Not that I'm aware of. We've only discussed it briefly in the Discord. |
@bennypowers and @claviska id be happy to open one for official tracking. |
I'm looking to migrate a large angular library to lit, and was searching for an implementation of the ReactiveControllerHost. I've extended the prototype linked in the OP: prototype on stackblitz.
Especially the latter is very important for performance reasons. Angular runs change detection every time code finishes running in the Angular zone. If the controller runs in the zone, every event listener will automatically run in the Angular zone too, which is disastrous for event listeners like |
Closing this for now since we don't really plan on making more controller adapters for other frameworks at the moment. If it comes up again, we know we can for most frameworks. |
If someone insterested in Vue3, here an SB repo using Vite instead Vue CLI, using both Options and Composition API: check the README.md and the https://stackblitz.com/edit/vitejs-vite-22tb3u?file=README.md /cc @justinfagnani current Vue SB playground not working |
We would like to enable a framework-agnostic subset of reactive controller to work across frameworks. To do this we need two things from the frameworks: a way to emulate or map the framework lifecycle to the reactive controller lifecycle, and a native composition API or way to extend the framework component model to make adding controllers possible.
Many frameworks seem to have the basic lifecycle required by reactive controllers:
hostConnected
,hostDisconnected
,hostUpdate
,hostUpdated
, andhost.requestUpdate()
- or they can be emulated. Not all have a way of extending the component model thoughinitial render
ngAfterContentInit
onMounted
init
onMount
useLayoutEffect
ngOnDestroy
onUnmounted
didDestroyElement
onDestroy
hook body
ngOnChanges
onBeforeUpdate
willUpdate
beforeUpdate
useLayoutEffect
ngAfterContentChecked
onUpdated
didUpdate
afterUpdate
useState
not needed w/ zones?
getCurrentInstance().update()
writable store
useLayoutEffect
ngOnChanges w/ Promise
nextTick
Extendable
custom hook
mixins
composition API
Mixin or CoreObject?
lifecycle hooks and stores
✅ = There's a way to emulate
⚠️ ? = Unsure how to implement, but there's probably a way
🆘 = Seems like there isn't a way to emulate
We would like the mapping or controller wrapper to end up being as idiomatic as possible in the host framework. If the framework already has a composition API, like React hooks, we want to wrap reactive controllers into that API. If the framework doesn't have a similar API, then we could try to extend the component model with a subclass or mixin and add a way to declare controllers.
See #1532 for an example of creating a
useController()
hook for React that emulates and drives the reactive controller lifecycle. After wrapping a reactive controller withuseController()
, the resulting hook is used like any other hook:Definition:
Idiomatic usage:
The text was updated successfully, but these errors were encountered: