2

使用 MobX 时延迟加载属性的当前惯用方式是什么?

我已经为此苦苦挣扎了几天,自从严格模式成为一件事以来,我还没有找到任何好的例子。我喜欢严格模式的想法,但我开始认为延迟加载与它不一致(访问或观察属性应该触发加载数据的副作用,如果它不存在的话)。

这是我问题的症结所在,但要了解我是如何到达这里的,请继续阅读。

我当前设置的基础知识(没有发布大量代码):

反应组件 1 (ListView):componentWillMount

  1. componentWillMount & componentWillReceiveProps - 组件从路由参数(react-router)中获取过滤器值,将其保存为 ListView 上的可观察对象,并告诉商店根据它获取“提案”
  2. Store.fetchProposals 检查该请求是否已经发出(请求存储在 ObservableMap 中,通过序列化过滤器对象的键,因此两个相同的过滤器将返回相同的响应对象)。它在需要时发出请求并返回包含有关请求是否完成或有错误的信息的可观察响应对象。
    1. ListView 将可观察的响应对象保存为属性,因此它可以显示加载或错误指示器。
    2. ListView 有一个计算属性,它使用用于获取的相同过滤器对象调用 Store.getProposals
    3. Store.getProposals 是一个转换器,它接受一个过滤器对象,从 ObservableMap 获取所有提案(proposal.id 上的键),使用过滤器对象过滤列表并返回一个 Proposal[] (如果没有匹配过滤器,则为空,包括如果没有提案已加载)

这一切似乎运作良好。

问题是提案具有客户端和客户端 ID 的属性。Proposal.clientId 是与提案一起加载的字符串。我想等到客户端被实际访问以告诉商店从服务器获取它(假设它还没有在商店中)。在这种情况下,ListView 恰好显示了客户端名称,因此它应该在 Proposal 之后立即加载。

我最接近的方法是在提案的构造函数列表中设置一个自动运行,但其中一部分没有对我正在缩进的地方做出反应。(截断相关部分):

@observable private clientId: string = '';
@observable private clientFilter: IClientFilter = null;
@observable client: Client = null;

constructor(sourceJson?: any) {
    super(sourceJson);
    if (sourceJson) {
        this.mapFromJson(sourceJson);
    }
    //this one works. I'm turning the clientId string into an object for the getClients transformer 
    autorun(() => { runInAction(() => { this.clientFilter = { id: this.clientId }; }) });
    autorun(() => {
        runInAction(() => {
            if (this.clientId && this.clientFilter) {
                const clients = DataStore.getClients(this.clientFilter);
                const response = DataStore.fetchClients(this.clientFilter);
                if (response.finishedTime !== null && !response.hasErrors) {
                    this.client = clients[0] || null;
                    console.log('This is never called, but I should see a client here: %o', DataStore.getClients(this.clientFilter));
                }
            }
        })
    });
}

响应对象是可观察的:

export class QueryRequest<T extends PersistentItem | Enum> {
    @observable startTime: Date = new Date();
    @observable finishedTime: Date = null;
    @observable errors: (string | Error)[] = [];
    @observable items: T[] = [];
    @computed get hasErrors() { return this.errors.length > 0; }
    @observable usedCache: boolean = false;
}

我感觉我正在与系统作斗争,并且在构造函数中设置自动运行似乎并不理想。有人以合理的方式解决此模式吗?如果我的设置看起来很疯狂,我愿意接受关于整个事情的建议。


编辑 1:为清楚起见,删除了 @Mobx。


编辑 2:试图重新评估我的情况,我(再次)找到了优秀的 lib mobx-utils,它有一个可以满足我需要的lazyObservable函数。目前它看起来像这样:

client = lazyObservable((sink) => {
    autorun('lazy fetching client', () => {
        if (this.clientFilter && this.clientFilter.id) {
            const request = DataStore.fetchClients(this.clientFilter);
            if (request.finishedTime !== null && !request.hasErrors) {
                sink(request.items[0]);
            }
        }
    })
}, null);

这是有效的!

我想我需要那里的自动运行来根据这个对象的 clientId/clientFilter 属性进行更新(如果这个对象后来被分配给一个新的客户端,我希望更新lazyObservable)。我不介意惰性属性的一些样板,但我绝对愿意接受那里的建议。

如果这最终成为要走的路,我还将从同一个库而不是我的可观察请求对象中查看fromPromise 。不确定,因为我正在跟踪开始时间以检查是否过时。链接在这里以防其他人没有遇到它:)

4

1 回答 1

2

我一直在我的项目中使用不同的方法,并将其提取到一个单独的 npm 包中:https ://github.com/mdebbar/mobx-cache

这是一个简单的例子:

首先,我们需要一个 React 组件来显示客户端信息:

@observer
class ClientView extends React.Component {
  render() {
    const entry = clientCache.get(this.props.clientId)

    if (entry.status !== 'success') {
      // Return some kind of loading indicator here.
      return <div>Still loading client...</div>
    }

    const clientInfo = entry.value
    // Now you can render your UI based on clientInfo.
    return (
      <div>
        <h2>{clientInfo.name}</h2>
      </div>
    )
  }
}

然后,我们需要设置clientCache

import MobxCache from "mobx-cache";

function fetchClient(id) {
  // Use any fetching mechanism you like. Just make sure to return a promise.
}

const clientCache = new MobxCache(fetchClient)

这就是你需要做的。MobxCache 会在需要时自动调用fetchClient(id)并为你缓存数据。

于 2016-09-11T00:00:44.677 回答