Replies: 4 comments
-
Could this be related? #3692 What you will need is to pass the reference of your host (This is basically how a controller works), so when your timer ticks it should request the host to update. Here's an example: https://lit.dev/playground/#sample=examples%2Fdirective-template-content&gist=4ce90fcff352d613e01b2c46511cd7bb |
Beta Was this translation helpful? Give feedback.
-
Hi @vdegenne, Yeah, in some way it is related since at the end of the day the result I'd like to have is the same, triggering an update when a property change (I've also put a link to the initial discord conversation + the discussion previous to the one you linked above). To put more context on what I'd like to reach anyway I can describe my real use case a bit more deeply. Right now I'm trying to use context with a very complex class that is extending from other classes and have other classes injected on it as services. I'd like to observe some of the class member variables I've in it, but the stuff can go several level deep into the class. And this is just a small part of the app. i.e. I'm setting this property using different methods that are not simple setter, having to pass down the host to every method that requires an update is not very viable. The project I'm working born as a react app and I was using redux as the state manager. It is some sort of a DAW where several machines that ranges from midi controllers to sampler and synthesizers play at once. It has the possibility to store projects + a lot of features. It works well but It starts to be very heavy on performance when I have +8 different machines (especially some machines or when I was loading several effects on it). I thought then that I should probably do not rely on react and the virtual dom and I wanted to experiment with something lighter like svelte. I decoupled then all the logic in individual controllers and re-organise the code in a library. This has been a very heavy exercise but what can I say... I think it has been a well spent time, especially from a personal development perspective. What do not kill you make you stronger. Then I've seen that lit-element has a built-in context way of doing the stuff and it was (with all it's difference) very close to the redux pattern I was using, or at least I thought it was. Fortunately due to the redux-react pattern I've added in most of the "large class" methods a callback function that is triggered on task completion. And now this is where I'm triggering the requestUpdate() method. But I would really love to make parts of my class observables. In this exact moment I was experimenting with preact/signals but I didn't find a way to make it work as I'd like to. I've seen you suggested also to use redux in one of the discussions mentioned above. I know redux pretty well and probably I should at least give it a try to I'm pretty new to Lit but I must admit I love the fact to stay closer to the platform and the idea to be able to reuse the components I build in whatever environment/framework. If you could address me to some updated resources about lit and redux I'd be very gratefully. Thanks again for your suggestion and support. |
Beta Was this translation helpful? Give feedback.
-
Just for the records, I was able to observe a property change on an external class using a pretty naive method. Following you can see it in action in two different way. The first one is a version where it is not needed to add any function watchClassMemberVariable(target: any, key: string, callback: (newValue: any) => void) {
let value = target[key];
Object.defineProperty(target, key, {
get() {
return value;
},
set(newValue) {
value = newValue;
callback(newValue);
}
});
} @customElement('current-time-display')
class CurrentTimeDisplay extends LitElement {
@consume({context: timerContext, subscribe: true})
@property({attribute: false})
timer;
connectedCallback() {
super.connectedCallback()
watchClassMemberVariable(this.timer.currentTime,'value', () => this.requestUpdate())
}
render() {
return html`<div>${this.timer.currentTime.value}`;
}
} The second one uses a @customElement('timer-display')
class TimerDisplay extends LitElement {
@consume({context: timerContext, subscribe: true})
timer;
@property({attribute: false})
timerCount
connectedCallback() {
super.connectedCallback()
this.timerCount = this.timer.value
watchClassMemberVariable(this.timer,'value', (v) => this.timerCount = v)
}
render() {
return html`<div>${this.timerCount}`;
}
} Maybe the logic can be incapsulated in a decorator like @externalClassMemberProperty({ target: this.timer, key: 'value' }) Btw, I did play a bit with lit and redux and they definitely works good together, indeed I think I'll keep using redux for the non-aggresively-reactive part of my app. One thing I don't like is the way it plays with reactivity and the fact I have to reassign the variable to trig an update and this can bloat the component with properties making the code a bit too verbose. Btw I think that I may try to create a redux-store a like just using @lit-labs/context. At the end of the day redux in react is "just" a provider using context, I think most of its pattern can be reused. Anybody thought about it already or have developed something like this? Maybe this topic deserve a new discussion. |
Beta Was this translation helpful? Give feedback.
-
The following snippet works also with class member variables that are objects. function watchClassMemberVariable(instance: any, path: string, callback: (newValue: any) => void) {
const parts = path.split('.');
let target = instance;
for (let i = 0; i < parts.length - 1; i++) {
target = target[parts[i]];
}
const key = parts[parts.length - 1];
let value = target[key];
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
for (let prop in value) {
watchClassMemberVariable(value, prop, callback);
}
}
const originalSetter = Object.getOwnPropertyDescriptor(target, key)?.set;
Object.defineProperty(target, key, {
get() {
return value;
},
set(newValue) {
value = newValue;
callback(newValue);
if (originalSetter) {
originalSetter.call(target, newValue);
}
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
for (let prop in value) {
watchClassMemberVariable(value, prop, callback);
}
}
}
});
} Usage example connectedCallback() {
super.connectedCallback()
// Define the default values
this.nestedValue = this.contextInstance.nestedInstance.state.nestedValue
this.value = this.contextInstance.nestedInstance.value
// Watch for changes, the dot notation means the value is contained is part of an object
watchClassMemberVariable(this.contextInstance.nestedInstance, 'state.nestedValue', (value) => this.nestedValue = value)
watchClassMemberVariable(this.contextInstance.nestedInstance, 'value', (value) => this.value = value)
} I'm using it in my project and it does the job, I'm definitely going to dive deeper into it. If anybody has any opinion about this pattern and wants to share it, it would be really nice to hear to it. |
Beta Was this translation helpful? Give feedback.
-
Hi everybody,
I was playing a bit with @lit-labs/context and I really like it but unfortunately I found some limitations that I hope are related with the fact I'm not a deep lit expert.
I'd like to know if it is possible to make reactive the class member variables of a class registered in context and consumed by another element.
I've tried to mirror the value into a state/property variable in the element but looks like it is not working.
Following a playground that I hope can help to understand the problem I'm facing.
Lit Playground
In this playground I'd like to find a way to trig a re-render when the counter and the currentTime gets updated.
Naturally this is a very simplified example. In one of my real use case I was able to solve it triggering the requestUpdate on the user interaction but this is not something I can do everywhere since the variable update can happen via side effects.
Thanks a lot for your help and support.
[Note] I've also try to pass a property down to the context since my class constructor was accepting it but looks like a value updated in a different controller do not works good with reactivity updates.
--Thread originally started on Discord
✏️ EDIT
I just dig further in the discussions panel and I've noted that the original discord thread has already a discussion opened over there. I'm definitely going to try the custom context approach but I also think that the current discussion can lead to different solutions, especially when dealing, like in my case, with classes that I can't or I prefer to keep as they are. #3685
Beta Was this translation helpful? Give feedback.
All reactions