如何处理 Flux 商店中的一对多关系

IT技术 reactjs reactjs-flux flux redux
2021-04-28 04:10:03

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

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

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

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

简而言之:

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

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

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 module,该module将包含与保留客户端 ID 和请求/响应的闭包相关的所有方法。动作创建者(或商店)可以调用这个 WebAPIUtils module来发起请求。

所以你有三个动作:

  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:
    // ...
}

请不要太关注名称的细节和这个实现的细节(可能有错别字或其他错误)。这只是解释该模式的示例代码。