【问题标题】:How to handle one-to-many relationships in Flux stores如何处理 Flux 商店中的一对多关系
【发布时间】:2015-10-16 22:51:24
【问题描述】:

我刚开始使用flux(现在使用redux)并且想知道应该如何处理关系。
举个例子,我们可以使用 Trello,它的看板带有包含卡片的列。

一种方法是为板子设置一个 store/reducer,并在其中保存所有数据,但这意味着一些非常大的存储,因为它们还必须包含列和卡的所有操作。

我见过的另一种方法是将嵌套资源分离为例如 BoardStore、ColumnStore 和 CardStore,并使用它们的 id 作为参考。

这是一个我有点困惑的例子:您可以有一个名为 addCard 的操作创建器,它向服务器发出请求以创建包含所有数据的卡片。如果您正在进行乐观更新,那么您之前会在您的一个商店中创建一个卡片对象,但在您收到请求之前您无法知道它的 ID。

简而言之:

  • 触发 addCard
  • addCard 执行请求,同时您返回 ADD_CARD_TEMP 类型的操作
  • 您收到请求并返回 ADD_CARD 类型的操作,其中 store/reducer 更改了 ID。

有处理这种情况的推荐方法吗?嵌套的 store/reducer 对我来说看起来有点傻,但否则你最终会得到非常复杂的 store,所以它看起来真的是一种妥协。

【问题讨论】:

    标签: reactjs reactjs-flux flux redux


    【解决方案1】:

    是的,像关系数据库一样跨多个存储使用 id 是正确的方法。

    在您的示例中,假设您希望乐观地将一张新卡片放在特定列中,并且一张卡片只能放在一列中(一列对多张卡片)。

    您 CardStore 中的卡片可能如下所示:

    _cards: {
      'CARD_1': {
        id: 'CARD_1',
        columnID: 'COLUMN_3',
        title: 'Go to sleep',
        text: 'Be healthy and go to sleep on time.',
      },
      'CARD_2': {
        id: 'CARD_2',
        columnID: 'COLUMN_3',
        title: 'Eat green vegetables',
        text: 'They taste better with onions.',
      },
    }
    

    请注意,我可以通过 id 引用卡片,也可以在对象中检索 id。这使我可以使用getCard(id) 之类的方法,并且还能够在视图层中检索特定卡片的 id。因此我可以有一个方法deleteCard(id) 来响应一个动作,因为我知道视图中的id。

    在卡片存储中,您将拥有getCardsByColumn(columnID),这将是卡片对象上的一个简单映射,这将生成一个卡片数组,您可以使用这些卡片来呈现列的内容。


    关于乐观更新的机制,以及 id 的使用对其有何影响:

    您可以使用在处理 XHR 响应的同一闭包中建立的客户端 ID,并在响应成功返回时清除客户端 ID,或者在错误时回滚。闭包允许您保留客户端 ID,直到响应返回。

    很多人会创建一个 WebAPIUtils 模块,其中包含与闭包相关的所有方法,保留客户端 ID 和请求/响应。动作创建者(或商店)可以调用此 WebAPIUtils 模块来发起请求。

    所以你有三个动作:

    1. 发起请求
    2. 处理成功
    3. 处理响应

    响应发起请求的操作,您的商店接收客户端 ID 并创建记录。

    作为对成功/错误的响应,您的商店会再次接收客户端 ID,然后将记录修改为具有真实 ID 的已确认记录,或者回滚记录。您还希望围绕该错误创建良好的用户体验,例如让您的用户重试。

    示例代码:

    // Within MyAppActions
    cardAdded: function(columnID, title, text) {
      var clientID = this.createUUID();
      MyDispatcher.dispatch({
        type: MyAppActions.types.CARD_ADDED,
        id: clientID,
        columnID: columnID,
        title: title,
        text: text,
      });
      WebAPIUtils.getRequestFunction(clientID, "http://example.com", {
        columnID: columnID,
        title: title,
        text: text,
      })(); 
    },
    
    // Within WebAPIUtils
    getRequestFunction: function(clientID, uri, data) {
      var xhrOptions = {
        uri: uri,
        data: data,
        success: function(response) {
          MyAppActions.requestSucceeded(clientID, response);
        },
        error: function(error) {
          MyAppActions.requestErrored(clientID, error);
        },
      };
      return function() {
        post(xhrOptions);
      };
    },
    
    // Within CardStore
    switch (action.type) {
    
      case MyAppActions.types.CARD_ADDED:
        this._cards[action.id] = {
          id: action.id,
          title: action.title,
          text: action.text,
          columnID: action.columnID,
        });
        this._emitChange();
        break;
    
      case MyAppActions.types.REQUEST_SUCCEEDED:
        var tempCard = this._cards[action.clientID];
        this._cards[action.id] = {
          id: action.id,
          columnID: tempCard.columnID,
          title: tempCard.title,
          text: tempCard.text,
        });
        delete this._cards[action.clientID];
        break;
    
      case MyAppActions.types.REQUEST_ERRORED:
        // ...
    }
    

    请不要过于关注名称的细节和此实现的细节(可能存在拼写错误或其他错误)。这只是解释模式的示例代码。

    【讨论】:

    • 感谢您的回复。抱歉,如果我不清楚,但我的问题更多是如何组织商店,我使用乐观更新作为通过 id 引用某些内容的可能问题的示例。
    • 对不起。我更新了我的答案。希望能回答你的问题。
    猜你喜欢
    • 2015-07-26
    • 2016-01-27
    • 2015-06-17
    • 2021-04-23
    • 1970-01-01
    • 2021-12-14
    • 1970-01-01
    • 2021-12-30
    相关资源
    最近更新 更多