MobX 不重新渲染 React 组件,因为使用的 @computed 已更改

MobX not rerendering React component as used @computed is changed

MobX 似乎不适合我。我基本上有这两个文件:

@inject("apiDepo")
@observer
class Test extends React.Component {
  public render() {
    return <div>{this.props.apiDepo.remoteNum}</div>
  }
}

.

// apiDepo.ts
class apiDepo {
  @observable private _remoteNum;

  @computed get remoteNum() {
    console.log("get")
    return this._remoteNum || (this.fetchRemoteNum() && undefined);
  }

  private async fetchRemoteNum() {
    const response = await someFuncThatRequestsARemoteNum();
    console.log("set")
    this._remoteNum = response.remoteNum || 0;
    console.log(this);
    console.log("remoteNum", this.remoteNum);
  }
}

运行控制台的输出是

> "get"
> "set"
> { _remoteNum: 0, fetchRemoteNum() } // is missing this.remoteNum
> "remoteNum" undefined // should be 0

后面没有了。

据我所知,这是 MobX 应该如何工作的一个非常简单的示例。你得到一个可观察的,它得到更新,应该调用 forceUpdate() (但不是)。

我想知道的是为什么它不能工作?上面的实现有什么不对劲吗?如果没有,有人知道为什么它不起作用吗?

编辑:我没有任何重载 componentShouldUpdate 除了@observer

编辑 2:添加了 console.log(this.remoteNum)

出问题的行是这里:

return this._remoteNum || (this.fetchRemoteNum() && undefined);

原因是,当 _remoteNum 等于 0 时,它是假的(因为它将数字转换为布尔值。0 被解释为假)。所以它不会 return 号码,而是再次调用 fetch!通过在每次通话后 return 拨打一个新号码,您可以轻松地看到此行为:

const response = await new Promise<number>((resolve) => {
            window.setTimeout(() => resolve(this._remoteNum++), 1000);
})

要防止此布尔值转换,您需要明确比较该值。

所以解决方案是:

@observable _remoteNum: number;

@computed get remoteNum(): number {
    if(this._remoteNum != null) {
        return this._remoteNum;
    } else {
        this.fetchRemoteNum() && undefined;
    }
}

private async fetchRemoteNum() {
    const response = await new Promise<number>((resolve) => {
        window.setTimeout(() => resolve(0), 1000);
    })

    console.log('updated');

    this._remoteNum = response;
}

我建议始终使用 === 或 !== 并且不要让 javascript 隐式地将值转换为 boolean