Search code examples
javascriptreactjsmobxmobx-reactmobx-state-tree

Using mobx store only outside of the react components render function


I have a react component that wraps a class that renders WebGL using three.js with the DOM and connects mobx store value and it changes with the class lifecycle methods.

The passed in mobx store is only used outside of the components render function in lifecycle functions (componentDidMount, componentDidUpdate, ..). Noticed that when the store changes, the component doesn't trigger a rerender. But I make a useless read within the render functions, such as in the example below passing a triggerRerenderListenerProp={this.props.store.debugSettings.showStats} prop to the div, the component becomes active only to store.debugSettings.showStats changes.

Is there a way of making the component listen to store changes wihtout using the store itself in the render function?

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {observer} from 'mobx-react';

import MapPreview from 'classes/MapPreview';

import style from './Preview.css';

class Preview extends Component {
  static propTypes = {
    store: PropTypes.object.isRequired,
    imageUrl: PropTypes.string.isRequired
  };

  constructor (props) {
    super(props);

    this.containerEl = null;
  }

  componentDidMount () {
    const options = {
      debugSettings: this.props.store.debugSettings,
      previewSettings: this.props.store.previewSettings
    };

    this.preview = new MapPreview(this.containerEl, options);
    this.preview.setImage(imageUrl);
  }

  componentDidUpdate () {
    this.preview.updateOptions({
      debugSettings: this.props.store.debugSettings,
      previewSettings: this.props.store.previewSettings
    });
  }

  render () {
    return (
      <div
        className={style.normal}
        ref={(el) => { this.containerEl = el; }}
        triggerRerenderListenerProp={this.props.store.debugSettings.showStats}
      />
    );
  }
}

export default observer(Preview);

Solution

  • The problem ultimately has two issues:

    • One, React is designed to only re-render when state or prop data changes.
    • Two, with mobx-react, I'm pretty sure the whole point is that the component won't re-render unless you dereference an observable value.

    So while your props are technically changing, React doesn't do a deep object comparison of the props.

    What you might try is setting options as internal component state -- that might force a re-render even though nothing in the render method would have changed.

    The caveat here is that the updated props (from your store) might be too deeply nested as to force React to re-render even while updating internal state. You might also need to piggy-back on shouldComponentUpdate();