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

时间:2020-07-17 23:19:07

标签: reactjs typescript redux api-design redux-thunk

我想允许用户多次将同一服务器项添加到页面中(标识: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动作创建者中请求数据:

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建议我们必须先生成唯一的ID,然后才能将其击中减速器。

问题:我如何适应这种模式以在化简器之前解析响应,同时保持灵活性以读取嵌套的api数据并解析化简器中的响应主体错误?

1 个答案:

答案 0 :(得分:1)

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

这里实际上只有一个数据流

  • 当用户单击“添加轮胎”(/tire/add/5)时,React应用程序可以从数据库请求轮胎。
  • 他们可以多次请求相同的轮胎
  • 然后他们可以修改该轮胎的每个实例以具有独特的属性
  • 点击“保存”会将汽车及其轮胎发送到/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响应