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

懵懂的女人 提交于 2019-12-04 23:26:37

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.

mweststrate

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))

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;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!