12

Using Mobx, after updating the store (i.e. clicking the button) the component does not re-render. I've installed mobx devtools which shows nothing after the initial load, and there is no error in the console. Any ideas what I've done wrong?

Store.js:

import { observable } from 'mobx';

class Store {

    @observable me;

    constructor() {
        this.me = 'hello';
    }

    change_me(){
        this.me = 'test 1234';

    }

}


export default Store;

layout.js:

import React from "react";
import { observer } from 'mobx-react';


@observer
export default class Layout extends React.Component{

    render(){

        return(
            <div>
                <h1>{this.props.store.me}</h1>
              <button onClick={this.on_change}>Change</button>
            </div>
        )
    }

    on_change = () => {
        this.props.store.change_me();
    }
}

index.js:

import React from "react";
import ReactDOM from "react-dom";
import Layout from "./components/Layout";
import Store from "./Store";
import DevTools, { configureDevtool } from 'mobx-react-devtools';

// Any configurations are optional
configureDevtool({
    // Turn on logging changes button programmatically:
    logEnabled: true,
    // Turn off displaying conponents' updates button programmatically:
    updatesEnabled: false,
    // Log only changes of type `reaction`
    // (only affects top-level messages in console, not inside groups)
    logFilter: change => change.type === 'reaction',
});


const app = document.getElementById('app');
const store = new Store();

ReactDOM.render(

    <div>
        <Layout store={store} />
        <DevTools />
    </div>
, app);
Chris
  • 1,557
  • 5
  • 19
  • 36
  • I copy pasted your code and it works in my environment. `after updating the store the component does not re-render` means after you click the button right ? – Max Nov 20 '16 at 09:35
  • Yep exactly. So it works for you? What else could it be? – Chris Nov 20 '16 at 09:39
  • Just a wild guess, check your import directory, whether its correct or not. – Max Nov 20 '16 at 09:48
  • Import directory? That would make the whole app not work at all wouldn't it? The app is working, just the update after clicking the button. – Chris Nov 20 '16 at 09:54
  • @Chris could you make a jsfiddle for this ? – rab Nov 20 '16 at 11:34
  • This looks all pretty standard. Is your project set up with decorators enabled? A fiddle (there are same base fiddles on the mobx readme you can use as base) or test repo could help. Did you check the browser logs for exceptions? – mweststrate Nov 20 '16 at 15:35
  • Can you share your .babelrc setup? – mweststrate Nov 20 '16 at 16:15
  • 2
    Have you read [this](https://github.com/mobxjs/mobx-react/issues/41)? You might need to put `transform-decorators-legacy` first in your list of plugins. – Tholle Nov 21 '16 at 10:31
  • http://stackoverflow.com/questions/40702028/react-mobx-this-is-null-when-trying-to-update-store/43650135#43650135 – Eduard Jacko Apr 27 '17 at 06:31
  • Thank you @Tholle! That saved me another several hours of frustration I'm sure :-) – Sam A. Horvath-Hunt Aug 09 '17 at 22:10
  • @SamHH Great! No problem. :) – Tholle Aug 10 '17 at 00:43

4 Answers4

2

I would start by adding @action to your change_me() function. From what I understand, it's not always completely required, but I have encountered problems like this in my own code several times when I've forgotten to add it.

Additionally post your .babelrc as @mweststrate suggested, as it will help others to check that the proper plugins are loaded.

cdoc
  • 21
  • 2
2

My guess would be to have uninitialized @observable. It is very counter-intuitive, but Babel doesn't handle those well. Even adding @observable me = undefined might help (see the generated js code when you assign something there. Generally I'd remove constructor completely and move the initialization to declaration (i.e. @observable me = "hello" an no constructor). It should then work fine.

Nopik
  • 542
  • 1
  • 4
  • 15
2

Just add makeObservable(this); in constructor function like below

    constructor() {
       makeObservable(this);
    }
-1

Watch the binding of the this context.

<button onClick={this.on_change}>Change</button>

the this reference will not be to the class, so likely when you are actually clicking it is going to say something along the lines of no props on undefined. Changing to:

  <button onClick={this.on_change.bind(this)}>Change</button>

should fix it. Or better yet, bind the context in the constructor so its not re-binding on every render

 constructor(props) {
   super(props)
   this.on_change = this.on_change.bind(this)
 }

then you can go back to your

Simone Poggi
  • 1,448
  • 2
  • 15
  • 34
dpastoor
  • 146
  • 8
  • 3
    This is very wrong answer, for 2 reasons. First, on_change is bound to this, by the way it is declared, so original call by {this.on_change} is good. Then, having .bind() inside {} is an anti-pattern, leading to downgraded performance, and should never be used. – Nopik Nov 20 '16 at 22:26
  • 1
    @Nopik Not actually true. `on_change` is not bound to `this`. React autobinds similar methods but only if the component is declared through `createClass`, not when ES6 classes are used. – Sulthan Nov 21 '16 at 12:23
  • @Nopik as sultan says, you really need to read up on your binding. Also, as I demonstrate below, the proper pattern is to bind in the constructor. You can read more here as well: http://egorsmirnov.me/2015/08/16/react-and-es6-part3.html – dpastoor Nov 21 '16 at 18:25
  • 8
    **`on_change` is already bound** because it’s written this way: `on_change = () => { ... }` which causes a binding during instantiation. See [ES Public Class Fields](https://tc39.github.io/proposal-class-public-fields/) proposal for more info. – Thai Nov 22 '16 at 07:00
  • 1
    @dpastoor 1) Sulthan is wrong, 2) the very link you've posted lists the method used as method 4, as a valid way of binding to this (just not in the standard yet, apparently OP uses extensions) – Nopik Nov 22 '16 at 22:37