15

我正在使用 MobX 2.2.2 尝试在异步操作中改变状态。我将 MobX 的 useStrict 设置为 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)
  });
}

我得到:

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

我是否需要将 @action 装饰器提供给第二个 .then 语句?任何帮助,将不胜感激。

4

3 回答 3

20

我是否需要将 @action 装饰器提供给第二个 .then 语句?任何帮助,将不胜感激。

这与实际解决方案非常接近。

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

应该

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

请记住,action可以通过多种方式调用@action. 从有关行动的文档中

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

都是将函数标记为动作的有效方法。

这是一个展示解决方案的 bin: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()

至于为什么你的错误实际上会发生,我猜顶层@action不会递归地装饰任何函数作为它正在装饰的函数内部的动作,这意味着传递给你的 promise 的匿名函数并不是真正的action.

于 2016-05-31T20:47:49.940 回答
10

补充上述答案;实际上,action仅适用于您传递给它的函数。中的函数在then单独的堆栈上运行,因此应该可以识别为单独的操作。

请注意,您还可以为操作命名,以便在使用这些操作时在 devtools 中轻松识别它们:

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

于 2016-05-31T21:02:41.067 回答
4

请注意,在异步方法中,您必须在等待某些内容后手动启动新的操作/事务:

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

我的偏好

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;
}
于 2018-05-14T11:54:44.743 回答