I'm fairly new to using MobX and have been trying to use it to detect window resize events in my React-Typescript app. Here's my code:
App.tsx:
import { computed } from "mobx";
import { observer } from "mobx-react";
import type { Breakpoint } from "./display_size_observer";
import { DisplaySizeObserver } from "./display_size_observer";
import "./styles.css";
const ChildComponent = ({ size }: { size: Breakpoint }) => {
return <p>Size: {size}</p>;
};
export default function App() {
const displaySize = computed(() => DisplaySizeObserver.size);
const Child = observer(() => <ChildComponent size={displaySize.get()} />);
return <Child />;
}
display_size_observer.ts:
import { observable, action } from "mobx";
export type Breakpoint = "small" | "medium" | "large";
const BreakpointSmall: number = 375;
const BreakpointMedium: number = 768;
const getSizeAsBreakpoint = (size: number) => {
if (size <= BreakpointSmall) {
return "small";
} else if (size <= BreakpointMedium) {
return "medium";
}
return "large";
};
const getWindowSize = () => {
return getSizeAsBreakpoint(window.innerWidth);
};
const DisplaySizeObserverFactory = (() => {
let instance: DisplaySizeObserver;
class DisplaySizeObserver {
@observable.ref
size: Breakpoint = getWindowSize();
constructor() {
window.addEventListener("resize", this.handleResize);
}
@action
private handleResize() {
const newSize = getWindowSize();
if (!this.size || this.size !== newSize) {
this.size = newSize;
console.log("Updating size to:", this.size);
}
}
}
return {
getInstance: () => {
if (!instance) {
instance = new DisplaySizeObserver();
}
return instance;
}
};
})();
export const DisplaySizeObserver = DisplaySizeObserverFactory.getInstance();
Link to codesandbox: https://codesandbox.io/s/admiring-mendel-h0kcb?file=/src/display_size_observer.ts:0-1169&resolutionWidth=446&resolutionHeight=675
According to my understanding, having the ChildComponent
within an observer
should track the update to the display size and re-render the Child
accordingly. However, this doesn't seem to be happening.
I would appreciate any help in understanding what I've done wrong and how to fix it.
Several things goes wrong:
add makeObservable(this);
inside class constructor. It is required since MobX v6. More info: mobx-react observer don't fires when I set observable
handleResize
function losing context here: window.addEventListener('resize', this.handleResize);
, because it is not bound nor arrow function. Make it arrow function.
Don't use @observable.ref
for size
, just @observable
is enough
Every component that uses observable
values should be wrapped into observer
HOC. Don't do this wierd things you do in App, just rewrite it like that:
const ChildComponent = observer(() => {
return <p>Size: {DisplaySizeObserver.size}</p>;
});
export default function App() {
return <ChildComponent />;
}
Or if you want ChildComponent
to accept props then wrap App inside observer
and just pass props:
const App = observer(() => {
return <ChildComponent size={DisplaySizeObserver.size} />;
})
export default App