6

For those who has written apps with mobx + react, I'm wondering if there's a better way to handle context issue (eg. this. returns undefined in mobx store) when using onClick event handler inside a react component w/ inject & observer.

I have been writing the handler like onClick={actionFromStore.bind(this.props.theStore)} to resolve that issue, but it seems like there should be more concise way to do this that I'm not aware of.

I'm not a mobx expert, any advice would be appreciated!

The actions here are async fetch requests

Bi Yoo
  • 249
  • 4
  • 9

3 Answers3

6

You can either use @action.bound decorator:

@action.bound
doSomething(){

    // logic

}

or use labmda function which will preserve the context:

@action
doSomething = ()=> {

    // logic

}
Nir Weber
  • 151
  • 5
  • 1
    This looks more like what I was looking for, I will test it out and check this answer if works. Thanks! – Bi Yoo Jan 11 '19 at 16:57
4

Since there is 2018, the best practice in React apps development is to use lambda functions as class properties instead of class methods.

The lambda function as class property resolves all issues that can happen with context. You don't have to bind methods to the specific context, if using it.

For example, you working with this in some class method:

export default class SomeClass {
    myProp = "kappa"

    myMethod() {
        console.log(this.myProp)
    }
}

In this case, if you will use it, e.g., like some event listener, this will be unexpectedly (actually, more than expected) change from SomeClass instance to other value. So, if you using class methods, you should modify you code like this:

export default class SomeClass {
    constructor() {
        this.myMethod = this.myMethod.bind(this)
    }

    myProp = "kappa"

    myMethod() {
        console.log(this.myProp)
    }
}

In constructor you are binding your class method to context of SomeClass instance.

The best way to avoid this kind of unnecessary code (imagine, that you have 10+ of this type of methods - and you should bind each of them), is to simply use lambda functions:

export default class SomeClass {
    myProp = "kappa"

    myMethod = () => {
        console.log(this.myProp)
    }
}

That's it! Lambda functions have no context, so this will always point to the SomeClass instance. So, now you can decorate you class property as you wish:

export default class SomeClass {
    myProp = "kappa"

    @action
    myMethod = () => {
        console.log(this.myProp)
    }
}

Note, that if you are using Babel, you have to use transform-class-properties plugin.

This question is more related to the core of JavaScript, so I advise you to read this MDN article for more information about this behavior.

Hope, this was helpful!

Limbo
  • 2,123
  • 21
  • 41
  • I am aware of [JS this](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this#Function_context). But I think my issue is more related to `mobx` + `react` . Let me see if I can reproduce the issue when I get a chance – Bi Yoo Jan 11 '19 at 16:55
  • @BiYoo context problems are always the vanilla-JS-related ;) The problem you described could happen even if you used any another libraries combo (e.g. `RxJS` + `React` or `mobx` + `Vue`, etc.) – Limbo Jan 12 '19 at 11:08
  • 1
    You were right, it was the `async` function that I had thought I was using it as anonymous function in the `mobx` store. It indeed was vanilla JS that was causing this behavior. Just using `bound` in the `mobx` library fixed the issue (I could've used lambda func but using `bound` read more concise and understandable in our codebase) . Thanks! – Bi Yoo Jan 14 '19 at 02:41
3

With Mobx 6, decorators are becoming more discouraged and cumbersome to use (requiring makeObservable(this) to be called carefully in the constructor, even in subclasses.)

I therefore now find it cleaner to use

doStuff = action(() => {
    //  stuff logic
})

rather than

@action.bound
doStuff() { ...

or

@action
doStuff = () => { ...

This pattern with no decorators also works in older Mobx versions.

thund
  • 1,842
  • 2
  • 21
  • 31