Using the MobX @action decorator with async functions and .then

后端 未结 3 697
一整个雨季
一整个雨季 2021-02-19 05:02

I\'m using MobX 2.2.2 to try to mutate state inside an async action. I have MobX\'s useStrict set to true.

@action someAsyncFunction(args) {
  fetch(`http://loca         


        
相关标签:
3条回答
  • 2021-02-19 05:15

    note that in async method you manualy have to start a new action/transaction after awaiting something:

    @mobx.action async someAsyncFunction(args) {
      this.loading = true;
    
      var result = await fetch(`http://localhost:8080/some_url`, {
        method: 'POST',
        body: {
          args
        }
      });
      var json = await result.json();
      @mobx.runInAction(()=> {
         this.someStateProperty = json
         this.loading = false;
      });
    }
    

    my preference

    I prefer to not use @mobx.action/runInAction directly but always place it on an private method. And let public methods call private methods that actually update the state:

    public someAsyncFunction(args) {
      this.startLoading();
      return fetch(`http://localhost:8080/some_url`, {
        method: 'POST',
        body: {
          args
        }
      })
      .then(res => res.json())
      .then(this.onFetchResult);
    }
    
    @mobx.action 
    private startLoading = () => {
       this.loading = true;
    }
    
    @mobx.action 
    private onFetchResult = (json) => {
       this.someStateProperty = json;
       this.loading = false;
    }
    
    0 讨论(0)
  • 2021-02-19 05:18

    To complement the above answer; indeed, action only works on the function you pass to it. The functions in the then are run on a separate stack and should therefor be recognizable as separate actions.

    Note that you can also give the actions a name as well so that you easily recognize them in the devtools if you use those:

    then(action("update objects after fetch", json => this.someStateProperty = json))

    0 讨论(0)
  • 2021-02-19 05:29

    Do I need to supply the @action decorator to the second .then statement? Any help would be appreciated.

    This is pretty close to the actual solution.

    .then(json => this.someStateProperty = json)
    

    should be

    .then(action(json => this.someStateProperty = json))
    

    Keep in mind action can be called in many ways that aren't exclusive to @action. From the docs on action:

    • action(fn)
    • action(name, fn)
    • @action classMethod
    • @action(name) classMethod
    • @action boundClassMethod = (args) => { body }
    • @action(name) boundClassMethod = (args) => { body }

    are all valid ways to mark a function as an action.

    Here's a bin demonstrating the solution: http://jsbin.com/peyayiwowu/1/edit?js,output

    mobx.useStrict(true);
    const x = mobx.observable(1);
    
    // Do async stuff
    function asyncStuff() {
      fetch('http://jsonplaceholder.typicode.com/posts')
        .then((response) => response.json())
        // .then((objects) => x.set(objects[0])) BREAKS
        .then(mobx.action((objects) => x.set(objects[0])))
    }
    
    asyncStuff()
    

    As for why your error actually happens I'm guessing that the top level @action doesn't recursively decorate any functions as actions inside the function it's decorating, meaning your anonymous function passed into your promise wasn't really an action.

    0 讨论(0)
提交回复
热议问题