-
ProblemI'm looking for an unobtrusive way to call disposers on class ElementSize {
resizeObserver: ResizeObserver;
@observable rect: DOMRect;
constructor(element: HTMLElement) {
this.resizeObserver = new ResizeObserver(this.handleResize);
this.resizeObserver.observe(element);
this.rect = element.getBoundingClientRect().toJSON() as DOMRect;
}
destructor() {
this.resizeObserver.disconnect();
}
@action.bound private handleResize(entries: ResizeObserverEntry[]) {
this.rect = entries[0].target.getBoundingClientRect();
}
@computed get width() { return this.rect.width; }
} class ObservableRefObject<T extends HTMLElement> {
@observable current: T;
@action.bound handle(element: T | null) { this.current = element }
} class Popover {
triggerRef: ObservableRefObject<HTMLButtonElement> = new ObservableRefObject();
@computed get triggerSize() {
// this computed returns ElementSize instance for an element
// obtained asynchronously from React ref.
if(this.triggerRef.current) {
// Obviously, we need to call `destructor()` of this instance
// whenever it is recomputed and when it becomes unobserved.
return new ElementSize(this.triggerRef.current)
}
}
} Things I have tried
This is what I'm using now but it's clumsy. class Popover {
triggerRef: ObservableRefObject<HTMLButtonElement> = new ObservableRefObject();
currentTriggerSize: ElementSize | undefined;
@computed get triggerSize() {
this.currentTriggerSize?.destructor();
if(this.triggerRef.current) {
this.currentTriggerSize = new ElementSize(this.triggerRef.current);
// I'm not even sure this is legal inside of a reaction
onBecomeUnobserved(this, 'triggerSize', () => {
this.currentTriggerSize?.destructor();
})
return this.currentTriggerSize;
}
}
}
Wrap class Popover {
triggerRef: ObservableRefObject<HTMLButtonElement> = new ObservableRefObject();
currentTriggerSize: ElementSize | undefined;
@computedWithDestructor get triggerSize() {
if(this.triggerRef.current) {
return new ElementSize(this.triggerRef.current)
}
}
} export function computedWithDestruction(target, propertyName, descriptor) {
let initialized = false;
let instance;
const getter = descriptor.get;
descriptor.get = function() {
if (instance) {
instance?.destructor?.();
}
// This seems to be called before `this.triggerRef`
// is initialized, resulting in error :(
instance = getter.call(this);
if (!initialized) {
initialized = true;
onBecomeUnobserved(target, propertyName, () => {
instance = undefined;
instance?.destructor?.();
});
}
return instance;
};
computed(target, propertyName, descriptor);
}
I guess this would work well #2133 but I don't like my classes to depend on the Maybe there is some completely different pattern I'm missing, that would make this a non-issue. Thanks for your help! |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 10 replies
-
Not exactly sure what you are trying to acomplish, but can't you use |
Beta Was this translation helpful? Give feedback.
-
Hey sorry to arrive late to the party :) Regarding the issue with Please let my know how it plays out, I'd like to know if an elegant solution exists out there :) |
Beta Was this translation helpful? Give feedback.
-
Possibility to consider class ElementSize {
resizeObserver: ResizeObserver;
atom: Atom;
@observable observed: boolean = false,
@observable ref: ObservableRefObjec<HTMLElement>
constructor(ref: ObservableRefObject<HTMLElement>) {
this.ref = ref;
this.resizeObserver = new ResizeObserver(() => this.atom.reportChanged());
this.atom = mobx.createAtom(
'ElementSize',
() => this.observed = true
() => this.observed = false
)
autorun(() => {
if (this.ref.current && this.observed) {
this.resizeObserver.observe(this.ref.current);
} else {
this.resizeObserver.disconnect()
}
})
}
@computed get width() {
this.atom.reportObserved();
return this.ref.current.getBoundingClientRect().width;
}
} Had to edit it few times, hopefully I got it right ... the idea should be clear |
Beta Was this translation helpful? Give feedback.
Possibility to consider