After switching from Redux to MobX for React I'm starting to extremely like MobX. It's pretty awesome.
MobX has a certain behavior where it won't update component if the provided store observable is not used in render. I think generally that's a pretty great behavior that makes components render only when something actually changed.
But... I do encountered couple of cases where I do not want or need to use MobX observable inside render method and in such cases my this.props.store
with MobX store won't get updated.
And in such cases, as a workaround, I just reference the observable in the render method, but I don't think that's a very clean approach, and I'm wondering if there's a cleaner way to do that?
This component code should explain even more what I'm exactly asking about. It's a component that changes body overflow style based on a MobX observable that I have in my store.
/*------------------------------------*\
Imports
\*------------------------------------*/
import React from 'react';
import connectStore from 'script/connect-store';
import addClass from 'dom-helpers/class/addClass';
import removeClass from 'dom-helpers/class/removeClass';
/*------------------------------------*\
Component
\*------------------------------------*/
class Component extends React.Component {
constructor(props) {
super(props);
this.constructor.displayName = 'BodyClassSync';
}
componentDidMount() {
this.checkAndUpdateMuiClass();
}
componentDidUpdate() {
this.checkAndUpdateMuiClass();
}
checkAndUpdateMuiClass() {
// This place is the only place I need latest MobX store...
if (this.props.store.muiOverlay) {
addClass(window.document.body, 'mod-mui-overlay');
}
else {
removeClass(window.document.body, 'mod-mui-overlay');
}
}
render() {
// This is my workaround so the componentDidUpdate will actually fire
// (with latest and updated store)
// when the 'muiOverlay' observable changes in the MobX store
// Is there any cleaner/better way to do this?
this.props.store.muiOverlay;
// This component doesn't and shouldn't render anything
return null;
}
}
/*------------------------------------*\
Export
\*------------------------------------*/
const ComponentWithStore = connectStore(Component);
export default ComponentWithStore;
(Note: I don't use @decorators, I connect store using ES5 syntax in the connectStore function. And it would be awesome if solution to this would be also in ES5.)
You could use an autorun in componentDidMount
and dispose the listener in componentWillUnmount
so that you don't have to reference it in the render method.
class Component extends React.Component {
constructor(props) {
super(props);
this.constructor.displayName = 'BodyClassSync';
}
componentDidMount() {
this.dispose = autorun(() => {
const { muiOverlay } = this.props.store;
if (muiOverlay) {
addClass(window.document.body, 'mod-mui-overlay');
} else {
removeClass(window.document.body, 'mod-mui-overlay');
}
});
}
componentWillUnmount() {
this.dispose();
}
render() {
return null;
}
}
Since you are not really doing anything in this component, you could put this autorun
directly in your store as well.
Not sure how I missed it, but thanks to @Tholle I've reread the "Reacting to observables" section in the MobX docs and found out the reaction
helper was the most suitable solution for me.
While, the reaction
was the most fitting for the exact problem I had. The autorun
and when
helpers are pretty similar in the function and I probably could use them for my use case too.
https://mobx.js.org/refguide/autorun.html
https://mobx.js.org/refguide/when.html
https://mobx.js.org/refguide/reaction.html
This is the code example that shows how to use reaction
for anyone that just wants quick copy+paste:
componentDidMount(){
this._notificationsReactionDispose = mobx.reaction(
() => this.props.store.notifications,
(notifications, reaction) => {
... the code that runs when "notifications" in my store change
}
);
}
componentWillUnmount() {
this._notificationsReactionDispose();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With