在哪里为 redux 应用程序中的实体生成唯一 ID?
Where to generate unique IDs for entities in a redux application?
我想允许用户多次将同一个服务器项目添加到页面 (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 操作创建者中请求数据:
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 建议我们必须在它到达 reducers 之前生成唯一 ID。
问题: 我如何调整此模式以在 reducer 之前解析响应,同时保持读取嵌套 api 数据和解析响应主体错误的灵活性减速器?
在对 Discord 进行进一步讨论后,OP 提供了以下关键信息:
There's really only one data flow piece here
- The React application can request a tire from database when user clicks "add tire" (
/tire/add/5
)
- They can request the same tire multiple times
- They can then modify each instance of that tire to have unique properties
- Click "save" will send the car and its tires to
/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()}
}
}
}
}
})
总体:
- 不要在 reducers 中做任何随机的事情
- 这意味着在您发送
之前生成任何随机内容
- 在调度包含该数据的操作之前,您可以根据需要预处理、修改和破坏 API 响应
我想允许用户多次将同一个服务器项目添加到页面 (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 操作创建者中请求数据:
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 建议我们必须在它到达 reducers 之前生成唯一 ID。
问题: 我如何调整此模式以在 reducer 之前解析响应,同时保持读取嵌套 api 数据和解析响应主体错误的灵活性减速器?
在对 Discord 进行进一步讨论后,OP 提供了以下关键信息:
There's really only one data flow piece here
- The React application can request a tire from database when user clicks "add tire" (
/tire/add/5
)- They can request the same tire multiple times
- They can then modify each instance of that tire to have unique properties
- Click "save" will send the car and its tires to
/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()}
}
}
}
}
})
总体:
- 不要在 reducers 中做任何随机的事情
- 这意味着在您发送 之前生成任何随机内容
- 在调度包含该数据的操作之前,您可以根据需要预处理、修改和破坏 API 响应