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

只愿长相守 提交于 2019-12-22 03:22:42

问题


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://localhost:8080/some_url`, {
    method: 'POST',
    body: {
      args
    }
  })
  .then(res => res.json())
  .then(json => this.someStateProperty = json)
  .catch(error => {
    throw new Error(error)
  });
}

I get:

Error: Error: [mobx] Invariant failed: It is not allowed to create or change state outside an `action` when MobX is in strict mode. Wrap the current method in `action` if this state change is intended

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


回答1:


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.




回答2:


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




回答3:


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;
}


来源:https://stackoverflow.com/questions/37554828/using-the-mobx-action-decorator-with-async-functions-and-then

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