最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to make MobX update the component when observable is not used in the render method? - Stack Overflow

programmeradmin4浏览0评论

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 ponent if the provided store observable is not used in render. I think generally that's a pretty great behavior that makes ponents 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 ponent code should explain even more what I'm exactly asking about. It's a ponent 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';
  }

  ponentDidMount() {
    this.checkAndUpdateMuiClass();
  }

  ponentDidUpdate() {
    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 ponentDidUpdate 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 ponent 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.)

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 ponent if the provided store observable is not used in render. I think generally that's a pretty great behavior that makes ponents 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 ponent code should explain even more what I'm exactly asking about. It's a ponent 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';
  }

  ponentDidMount() {
    this.checkAndUpdateMuiClass();
  }

  ponentDidUpdate() {
    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 ponentDidUpdate 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 ponent 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.)

Share Improve this question edited Jul 11, 2018 at 11:34 Dominik Serafin asked Jul 11, 2018 at 11:21 Dominik SerafinDominik Serafin 3,6863 gold badges19 silver badges29 bronze badges 7
  • 1 I apologize. I read it wrong and thought there were some formatting errors. I reverted it. – Tholle Commented Jul 11, 2018 at 11:28
  • 1 @Tholle ahh no problem. The #javascript tag is wele addition though, thanks! – Dominik Serafin Commented Jul 11, 2018 at 11:29
  • 1 You are checking for this.props.store.muiOverlay in checkAndUpdateMuiClass . Can it be set to an empty string outside of this ponent? – Tholle Commented Jul 11, 2018 at 11:29
  • 1 @Tholle in case of this exact ponent and muiOverlay observable - the value of that observable is always boolean – Dominik Serafin Commented Jul 11, 2018 at 11:31
  • 1 Did you find another solution for this? – Tholle Commented Aug 3, 2018 at 14:09
 |  Show 2 more ments

2 Answers 2

Reset to default 3

You could use an autorun in ponentDidMount and dispose the listener in ponentWillUnmount 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';
  }

  ponentDidMount() {
    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');
      }
    });
  }

  ponentWillUnmount() {
    this.dispose();
  }

  render() {
    return null;
  }
}

Since you are not really doing anything in this ponent, 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/refguide/autorun.html

https://mobx.js/refguide/when.html

https://mobx.js/refguide/reaction.html


This is the code example that shows how to use reaction for anyone that just wants quick copy+paste:

ponentDidMount(){
  this._notificationsReactionDispose = mobx.reaction(
    () => this.props.store.notifications,
    (notifications, reaction) => {
      ... the code that runs when "notifications" in my store change
    }
  );
}

ponentWillUnmount() {
  this._notificationsReactionDispose();
}

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论