-
-
Notifications
You must be signed in to change notification settings - Fork 397
Feat: Unobserve a state #1097
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
base: master
Are you sure you want to change the base?
Feat: Unobserve a state #1097
Conversation
Gnyblast
commented
Nov 11, 2025
- Currently there's set,get, delete and observe method for statemanager
- Delete method even though it says halts the observations, it doesn't do it really
- I experienced it on a Rangeloop created component that interacts with confirmation dialog and observe state from there.
- Observation getting created when dialog was opened and I was deleting it on close, but it looks like they keep observing and interacting with multiple component same way was stacking up the obvervations.
- I implemented unobserve method to overcome this and it seems to work.
- This is pretty much `subscribe` and `unsubscribe` in angular.
|
I what scenario would you need to unobserve a state? |
|
Sure let me explain, Let say I have a app-level confirmation dialog. These kind of things are generally injected only once to app and used ad-hoc. So, imagine I have components that are generated with So in short, there are states in app-wise that should have only 1 single observers: confirmation_dialog.go func (c *ConfirmationDialog) OnMount(ctx app.Context) {
var dialogState bool
ctx.ObserveState("confirmation", &dialogState).OnChange(func() {
if dialogState {
c.openConfirmation(ctx)
}
})
}
func (c *ConfirmationDialog) openConfirmation(ctx app.Context) {
doc := app.Window().Get("document")
mdc := app.Window().Get("mdc")
dialogElem := doc.Call("querySelector", "#confirmDialog")
dialog := mdc.Get("dialog").Get("MDCDialog").New(dialogElem)
c.onDialogClosing = app.FuncOf(func(this app.Value, args []app.Value) any {
reason := args[0].Get("detail").Get("action").String()
ctx.SetState("confirm_state", reason == "accept")
dialogElem.Call("removeEventListener", "MDCDialog:closing", c.onDialogClosing)
return nil
})
dialogElem.Call("addEventListener", "MDCDialog:closing", c.onDialogClosing)
dialog.Call("open")
}some_component.go func (p *Pod) deallocate(ctx app.Context, e app.Event) {
ctx.SetState("confirmation", true)
var confirmState bool
ctx.ObserveState("confirm_state", &confirmState).OnChange(func() {
fmt.Println(confirmState)
//TODO: Do deallocation
ctx.UnObserveState("confirm_state")
})
}Otherwise it will cause many troubles in the app, and this is not just my idea, I used almost this type of subscriptions in many places especially angular. And all provides unsubscribe method. |
|
@maxence-charriere any idea about this? Can it be achieved some other way? |
Maybe each button should have their own state. |
If there are like 100 repetition of same component then it does not sound very practical to generate a unique observestate names for each honestly. |
|
I think the interaction model becomes a lot simpler if each element manages its own confirmation flow. When you have a list of items and each one triggers a confirmation, the logic usually belongs to that specific element, the UI for that element should just react to its own state. Right now you’re trying to coordinate multiple components through one shared global state, and that forces every element to subscribe/unsubscribe and stay in sync with one another. That’s where the complexity and the risk of mixing things together comes from. If each element has its own state, you avoid multiple components listening to the same global key, and each one stays responsible for its own logic. The UI becomes a simple reaction to that state instead of a chain of state-based interactions across unrelated components. |
|
Also, the way go-app is designed, a component automatically stops observing a state when it’s dismounted. So if each element owns its own confirmation state, you naturally avoid having stale observers or multiple listeners firing at the same time. The framework already handles the cleanup for you. |
|
After that, I guess it doesn’t hurt to have a way to manually unobserve a state, but I’d rather avoid adding something that isn’t truly needed. Since go-app automatically stops observing when a component is dismounted, keeping the confirmation logic local to each element usually makes manual unsubscribe unnecessary. So if I add such a feature, I want to be sure it’s really required and not just working around a pattern that can be simplified. |
|
Yes I think you are right but what makes me feel uncomfortable with each components with their own state name is: I fetch this datalist from a database table then, range loop iterates over and creates many of these components depending on response, to make things work, I need to write some overhead code and a small cache to generate random prefix/suffix for each state name and also store it to that cache to prevent name conflict. Because it's fully automatic and you don't know how many there will be of these components, it's then another automated process of keeping track of names. Apart from that, I don't want to insert hidden confirmation dialog component to the UI for each of these components. It creates overhead rendering process, instead 1 global confirmation dialog would release a lot of pressure on UI. Maybe I can get single confirmation dialog to work with many different state names, if I pass the target state name on opening, maybe I can get it works. On the otherhand, I believe there will be need at somepoint for unobserving a state not that I'm having a scenario atm, but all these modern JS frameworks cannot be mistaken that it's implemented. I feel like there are exceptional cases that this might be needed. |