在 redux 应用程序中,在哪里为实体生成唯一 ID?

Posted

技术标签:

【中文标题】在 redux 应用程序中,在哪里为实体生成唯一 ID?【英文标题】:Where to generate unique IDs for entities in a redux application? 【发布时间】:2020-11-07 18:56:01 【问题描述】:

我想允许用户多次将同一个服务器项添加到页面(id:5)。为了在我们的应用程序中跟踪这些项目,我们需要为每个实例生成一个唯一的 ID。

我们的项目具有关系数据,因此出于性能原因,我们可能会在 redux 存储中标准化 API 响应。

type Item =  id: string; 
type Sku =  id: string; 

type AppStore = 
  items:  [id: string]: Items ,
  skus:  [id: string]: Skus ,
  itemSkus:  [id: string]: string[] ,

使用此 API

export interface IAPIResponse<T> 
  data?: T;
  errors?: string[];


type IAPIItem = 
  id: string;
  skus: Array<
    id: string;
  >;

在典型的 redux-thunk action creator 中请求数据:

export const addItem = () => async (dispatch) => 
  dispatch(startedAddItem());
  try 
    const response = await get <IAPIResponse<IAPIItem>>('/get/item/5');
    dispatch(succeededAddItem(response));
   catch (error) 
    console.error('Error', error);
    dispatch(failedAddItem(error));
  
;

并用他们的相关数据填充我们的减速器:

// items reducer
case ItemAction.addSucceeded:
  const apiItem = getApiResource(action.payload);
  const apiErrors = getApiErrors(action.payload);

  if (apiErrors) // Handle errors in state

  if (apiItem) 
    const item = buildItem(apiItem);
    return 
      ...state,
      [item.id]: item,
    
  
  break;

// skus reducer
case ItemAction.addSucceeded:
  const apiItem = getApiResource(action.payload);
  const apiErrors = getApiErrors(action.payload);

  if (apiErrors) // Handle errors in state

  if (apiItem) 
    const skus = buildSkus(apiItem.skus);
    const indexedSkus =  ...skus.map(s => ( [s.id]: s )) ;
    return 
      ...state,
      ...indexedSkus,
    
  
  break;

// itemSkus reducer
case ItemAction.addSucceeded:
  const apiItem = getApiResource(action.payload);
  const apiErrors = getApiErrors(action.payload);

  if (apiErrors) // Handle errors in state

  if (apiItem) 
    const item = buildLineItem(apiItem);
    const skus = buildSkus(apiItem.skus);

    return 
      [item.id]: skus.map(s => s.id),
    
  
  break;

在这种模式下,我们无法可靠地为 Item 和 Skus 生成相同的唯一 ID,因为响应在多个 reducer 中进行解析。 Redux 建议我们必须在它到达 reducer 之前生成唯一 ID。

问题:我怎样才能适应这种模式来解析reducer之前的响应,同时保持读取嵌套api数据和解析reducer中的响应体错误的灵活性?

【问题讨论】:

我对“具有多个实例的商店项目”评论感到困惑。您能否为您正在尝试做的事情以及为什么/如何做进一步的解释? @markerikson 抱歉,我已经更新了那部分 :) 假设您正在制造一辆汽车并想要添加四个轮胎。轮胎在服务器上具有相同的 ID。但是在我们的客户端应用程序中,我们需要为每个轮胎生成一个唯一的 id 来跟踪它。 那么从应用程序用户流中,您需要在这个序列中的哪个位置开始使用这些 ID? “多个实例”实际上是从服务器返回的,还是根据用户交互在客户端上随着时间的推移添加的(即“添加左前轮胎”)? @markerikson 他们从服务器回来,即。用户单击“添加轮胎”,然后出现一个对话框,他们从数据库中选择预先存在的轮胎。所以他们添加了四个相同的轮胎。在将轮胎添加到 redux 商店之前,我需要生成 ID 那么服务器是否真的返回了tires: [, , , ]?如果是这样,为什么不让服务器生成这些唯一 ID?还是我误解了这里的用法? (另请注意,目前这可能更容易在 Reactiflux #redux 频道中讨论)。 【参考方案1】:

在对 Discord 进行进一步讨论后,OP 提供了以下关键信息:

这里真的只有一个数据流片段

当用户单击“添加轮胎”时,React 应用程序可以从数据库中请求轮胎 (/tire/add/5) 他们可以多次请求同一个轮胎 然后他们可以修改该轮胎的每个实例以具有独特的属性 点击“保存”会将汽车及其轮胎发送至/car/save

我的回答:

/tire/add API 端点每次发送响应时都可以生成一个 UUID。或者,在客户端:

const addTire = (sku, otherData) => 
  const response = await post(`/tire/add/$sku`, otherData);
  response.data.id = uuid();
  dispatch(tireAdded(response.data));

或者,特别是如果您使用 RTK:

const tiresSlice = createSlice(
  name: "tires",
  initialState,
  reducers: 
    tireAdded: 
      reducer(state, action) 
        // whatever here
      ,
      prepare(tireData) 
        return 
          payload: ...tireData, id: uiud()
        
      
    
  
)

总体:

不要在减速器中随意做任何事情 这意味着在您调度之前生成任何随机的东西 在调度包含该数据的操作之前,您可以根据需要对 API 响应进行预处理、修改和修改

【讨论】:

您建议生成随机 ID,以便它们作为有效负载的一部分传递,而不是在 reducer 中生成? 是的,因为在 reducer 中生成 any 随机值使其不可预测,并且随机数生成被认为是“副作用”:redux.js.org/style-guide/…。现在,如果您在 reducer 中生成随机数,代码会运行吗?当然。但这不是首选方法。 感谢您的解释。我想一个 UI 组件可能需要负责确保任何 id 的全局唯一性呢?似乎最好留给减速器。 绝对不是我的建议 :) 例如,如果您需要在每次发送“添加项目”操作时生成唯一 ID,您可以在操作创建器中执行此操作。由于 RTK 为您生成动作创建者,因此它提供了“准备回调”作为这样做的一种方式:redux.js.org/tutorials/essentials/… 啊哈!我现在看到了不同。谢谢你:)

以上是关于在 redux 应用程序中,在哪里为实体生成唯一 ID?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 redux 中的 axios 库为发布到 API 服务器的数据自动生成唯一 ID

在一天内为实体生成唯一的序列号

在 React Redux 应用程序中,我在哪里从服务器获取初始数据?

使用 Redux,错误消息在哪里产生最好?

在同构 Redux 应用程序中在哪里设置 cookie?

在你的 redux 应用程序中哪里有套接字对象? [关闭]