Ananas
Ananas

Reputation: 5

MobX observables change do not always trigger observer components render

My project is built on React, and for state management Mobx is being used. We are not using the decorators, so the components that need to observe the observables, need to be wrapped in the following way:

import React from 'react';
import {observer} from 'mobx-react';
import MyComponent from '../app/my-component.jsx';
import myFilter from './my-filters.jsx';

export default observer(() => {
  return <MyComponent filter={myFilter}/>;
});

The component MyComponent is receiving the observables as props:

static propTypes() {
    return {
      myFilter: React.PropTypes.function.isRequired
    };
  }

And it is used in the render method:

render() {
  if (this.props.myFilter.filterValue1 !== null) {
    // some code here
  }
}

myFilter in this case is the observable, which looks somehow like this:

import {observable} from 'mobx';

const myFilter = observable({
  filterValue1: null,
  filterValue2: null,
  addOrRemoveItem() {
    // some function here
  }
});

export default myFilter;

In this case, if some component alters myFilter, the observer MyComponent which receives the observable as props, does not always re-render. In some cases this can be solved by addressing the observable object by attribute before the call of the component. E.g.:

export default observer(() => {
  console.log(myFilter.filterValue1);
  return <MyComponent filter={myFilter}/>;
});

But this is not stable. Is there a valid workaround to avoid this?

Upvotes: 0

Views: 1472

Answers (2)

Konstantin
Konstantin

Reputation: 25369

Declare MyComponent as an observer.

export in app/my-component.jsx should look like this

export default observer(MyComponent);

This piece of code

export default observer(() => {
  return <MyComponent filter={myFilter}/>;
});

turns an anonymous stateless component, not MyComponent, into an observer.

MobX documentation clearly articulates that you need to apply observer to all components that render observable data or you will encounter problems.

The other way to solve the problem would be passing plain data from this anonymous stateless component into MyComponent.

export default observer(() => {
  return <MyComponent filterValue1={myFilter.filterValue1}/>;
});

This way MyComponent will be rerendered because it receives new props each time its parent is rerendered.

Upvotes: 0

gius
gius

Reputation: 9437

Alik is right, you need MyComponent to be an observer as well.

Otherwise, your code <MyComponent filter={myFilter}/> inside the only observable you have means that you want refresh only when myFilter object (ie., reference to it) changes. It does not access any of its properties and thus refresh is not needed when those properties change. That's why your component was refreshed when you accessed the filterValue1 property in the console.log statement.

Upvotes: 0

Related Questions