【问题标题】:Idiomatic way to lazy-load with mobx使用 mobx 进行延迟加载的惯用方法
【发布时间】:2016-09-07 12:05:42
【问题描述】:

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

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

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

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

React 组件 1(列表视图): 组件WillMount

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

这一切似乎运作良好。

问题在于提案具有 client 和 clientId 的属性。 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: 试图重新评估我的情况,我(再次)找到了优秀的库 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,而不是我的可观察请求对象。不确定,因为我正在跟踪开始时间以检查是否过时。链接在这里以防其他人没有遇到它:)

【问题讨论】:

  • 如果需要,我很乐意发布更多代码,只是试图缩短文字墙。
  • 为什么所有东西都以@Mobx. 为前缀,你不能进行解构导入吗?
  • 你可以只在存储层缓存数据,在 Promise 中或者在你的 MobX 中有一个 Repository 类来处理接触服务器。一般来说,我发现自动运行表明代码存在更深层次的问题。
  • @BenjaminGruenbaum - 这就是我们使用的风格,所以很明显来自 Mobx。打算在评论中删除但忘记了:/
  • @BenjaminGruenbaum - (嘿,刚刚注意到两个 cmets 都是你 :) 我一拿到数据就将数据缓存在商店中。我试着沿着承诺的道路走了一会儿,但没有任何运气。你不会碰巧知道有一个项目可以做类似的事情,我可以将其视为一个例子?我同意自动运行指示问题,我正在尝试找出一种更好的方法来构建所有这些。谢谢!

标签: javascript reactjs lazy-loading mobx


【解决方案1】:

我一直在我的项目中使用不同的方法,并将其提取到一个单独的 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) 并为你缓存数据。

【讨论】:

  • 谢谢@Jakke!该库仍处于早期开发阶段,如果有任何反馈,我将不胜感激 :)
  • 有趣。但不好的是,在 render() 中发起的 fetch 打破了 React 习惯用法,使副作用为零 render()
猜你喜欢
  • 2021-07-26
  • 1970-01-01
  • 1970-01-01
  • 2018-08-30
  • 2011-01-09
  • 2017-07-08
  • 2013-04-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多