Let's say that i have code:
export default class Sound {
protected audio: HTMLAudioElement;
constructor(private url: string) {
this.audio = new Audio();
this.audio.preload = "auto";
this.audio.src = url;
}
play(): Promise<void> {
return this.audio.play();
}
stop(): void {
this.audio.pause();
this.audio.currentTime = 0;
}
pause(): void {
this.audio.pause();
}
get playing(): boolean {
return !this.audio.paused;
}
}
and i want to observe playing but without modyfy this class (lets say that class Sound is some library). We can do something like this:
import { observable } from "mobx";
import Sound from "./Sound";
export default class SoundStore extends Sound {
@observable playing = false;
constructor(private url: string) {
super(url);
this.audio.addEventListener("play", () => {
this.playing = true;
});
this.audio.addEventListener("pause", () => {
this.playing = false;
});
}
}
Example code: https://codesandbox.io/s/mobix-observe-raw-class-hxr12
My questions:
On Thu, 16 Jul 2020, 07:24 Łukasz Sałajczyk, notifications@github.com
wrote:
Let's say that i have code:
export default class Sound {
protected audio: HTMLAudioElement;constructor(private url: string) {
this.audio = new Audio();
this.audio.preload = "auto";
this.audio.src = url;
}play(): Promise
{
return this.audio.play();
}stop(): void {
this.audio.pause();
this.audio.currentTime = 0;
}pause(): void {
this.audio.pause();
}get playing(): boolean {
return !this.audio.paused;
}}and i want to observe playing but without modyfy this class (lets say
that class Sound is some library). We can do something like this:import { observable } from "mobx";import Sound from "./Sound";
export default class SoundStore extends Sound {
@observable playing = false;constructor(private url: string) {
super(url);this.audio.addEventListener("play", () => { this.playing = true; }); this.audio.addEventListener("pause", () => { this.playing = false; });}}
Example code: https://codesandbox.io/s/mobix-observe-raw-class-hxr12
My questions:
- It is a preferred way to do this?
- It is a way to do this but without creating a new class extended
from this base class?—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/mobxjs/mobx/issues/2400, or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAN4NBH6P7AXYIMIWBGJ55LR32MKDANCNFSM4O3UME3A
.
Yep, you have totaly right, but i have no idea how to write a code to make observable audio.paused from Sound in SoundStore. Any idea how to do this?
Okey, so the best should be when the code of Sound was:
export default class Sound extend EventEmitter {
protected audio: HTMLAudioElement;
constructor(private url: string) {
this.audio = new Audio();
this.audio.preload = "auto";
this.audio.src = url;
this.audio.addEventListener("play", () => {
this.emit("changeState", true);
});
this.audio.addEventListener("pause", () => {
this.emit("changeState", false);
});
}
play(): Promise<void> {
return this.audio.play();
}
stop(): void {
this.audio.pause();
this.audio.currentTime = 0;
}
pause(): void {
this.audio.pause();
}
get playing(): boolean {
return !this.audio.paused;
}
}
Then in store we can add listener to event changeState and update observable field. Then all is super easy :) Did you mean something like this?
You write also:
But you can you the decorate or extendObservable utilities to modify existing classes
Can you show some code to do this with this original Sound class?
On Thu, Jul 16, 2020 at 9:34 AM Łukasz Sałajczyk notifications@github.com
wrote:
>
1.
Yep, you have totaly right, but i have no idea how to write a code to
make observable audio.paused from Sound in SoundStore. Any idea how to
do this?
2.Okey, so the best should be when the code of Sound was:
export default class Sound extend EventEmitter {
protected audio: HTMLAudioElement;constructor(private url: string) {
this.audio = new Audio();
this.audio.preload = "auto";
this.audio.src = url;this.audio.addEventListener("play", () => { this.emit("changeState", true); }); this.audio.addEventListener("pause", () => { this.emit("changeState", false); });}
play(): Promise
{
return this.audio.play();
}stop(): void {
this.audio.pause();
this.audio.currentTime = 0;
}pause(): void {
this.audio.pause();
}get playing(): boolean {
return !this.audio.paused;
}}Then in store we can add listener to event changeState and update
observable field. Then all is super easy :) Did you mean something like
this?You write also:
But you can you the decorate or extendObservable utilities to modify
existing classesCan you show some code to do this with this original Sound class?
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/mobxjs/mobx/issues/2400#issuecomment-659250769, or
unsubscribe
https://github.com/notifications/unsubscribe-auth/AAN4NBBGS6UMZG3FSAV3EPTR323TDANCNFSM4O3UME3A
.
Maybe i not undersand, but where you want to add this code?
As I said, i don't want to modyfy original class Sound. So probably I need to add this in my ExampleStore
class ExampleStore {
@observable foo: HTMLAudioElement;
constructor(creator: Creator) {
const instance = new Sound("url");
this.foo = extendObservable(instance.audio, { paused: false });
}
}
const store = new ExampleStore();
Problems:
store,foo.paused is not udpating after run store.foo.start()ExampleStore to get access to method stop() or getter playingYeah I didn't expect it to work as stated above. I'm a little confused by this thread, I still don't get what is wrong with your original solution, it looks fine to me? Except that I won't extend Sound, but just delegate the api's you are interested in.
import { observable } from "mobx";
import Sound from "./Sound";
export default class SoundStore extends Sound {
@observable playing = false;
constructor(private url: string) {
super(url);
this.audio.addEventListener("play", () => {
this.playing = true;
});
this.audio.addEventListener("pause", () => {
this.playing = false;
});
}
}
Looking at original questions
It is a preferred way to do this?
Honestly, try not to look for a "holy grail" of doing something. Anything that works for you is totally fine.
It is a way to do this but without creating a new class extended from this base class?
If the Sound doesn't expose audio element or any sort of callbacks/listeners, then you have to extend it.
@mweststrate Am asking because when I reading documentation I saw this extendObservable and am wondering if this function is provided to this kind of problems in some way with I can't see. In this orginal solution, as you rightly noticed, is duplicated information about audio status. By default object of HTMLAudioElement has got flag paused (via new Audio().paused) but we can't observe it value. That's why we must duplicate this information in own flag with we can observe. Im only wondering if this duplication is needed or its any way to magicly do this by some MobX mechanism.
@FredyC Okay, this have totaly sense to me!
Thanks for discussion and Yours time :)
That's why we must duplicate this information in own flag with we can observe.
There is an abstraction called atom, which by itself doesn't have any value, but can be observed and notify observers about a change.
export default class SoundStore extends Sound {
constructor(private url: string) {
super(url);
this.playingAtom = mobx.createAtom('SoundStore.playing');
this.audio.addEventListener("play", () => this.playingAtom.reportChanged());
this.audio.addEventListener("pause", () => this.playingAtom.reportChanged());
}
get playing() {
this.playingAtom.reportObserved();
return !this.audio.paused;
}
}
Most helpful comment
There is an abstraction called
atom, which by itself doesn't have any value, but can be observed and notify observers about a change.