问题描述
我有一个深层嵌套的数据对象,它从我的API返回,看起来像下面的JSON。
我正在使用Redux工具包的createSlice
创建一个trip
的切片
因此,目前在我的createSlice
中,我想存储一系列旅行。
我还希望能够更新单个行程或行程的一部分
- 例如,假设我要更新旅行项目的开始日期
- 或者,假设我要更新旅行项目的会员姓名
我的问题和疑虑:
- 我目前所有这些实体都返回到
trip
createSlice中,但是我不确定将这些实体标准化后是否将它们分成单独的createSlice
?如果是这样,这是怎么做的?这是一种反模式吗? - 应如何在
initialState
中定义嵌套实体? - 我应该在
initalState
中定义所有规范化的实体吗? 如果这样做的话,我想更新trip_item
或trip_item_member
时我的异径管会是什么样子? - 我的标准化数据看上去是否“正确”?我已经省略了
mergeStrategy
和trips_items_members
之间的trip_members
,我知道我应该这样做,但是还没有弄清楚它是如何工作的,或者在这里是否有必要?
注意:
RTK文档here中有一个示例,显示createSlice
与3个单独的实体一起使用,这些实体最初来自1个API调用。看起来好像是3个独立的文件,但尚不清楚它们之间如何共享数据。
这就是我的旅行createSlice
的样子
/**
* Get trip by ID action
*/
export const getTripByID = createAsyncThunk(
'trips/getTripByID',async ({ uid }) => {
const response = await findOne(uid)
const normalized = normalize(response,trip)
return normalized.entities
},)
const tripsAdapter = createEntityAdapter({
selectId: entity => entity.trip_id,sortComparer: (a,b) => b.start_date.localeCompare(a.start_date),loading: '',error: '',data: [],})
export const {
selectById: selectTripById,selectIds: selectTripIds,selectEntities: selectTripEntities,selectAll: selectAllTrips,selectTotal: selectTotalTrips,} = tripsAdapter.getSelectors(state => state.trip)
const initialState = tripsAdapter.getinitialState()
const tripSlice = createSlice({
name: 'trips',initialState,extraReducers: builder => {
builder.addCase(getAllTrips.fulfilled,(state,{ payload }) => {
tripsAdapter.upsertMany(state,payload)
state.loading = false
})
builder.addCase(getTripByID.fulfilled,{ payload }) => {
console.log('payload',payload)
tripsAdapter.upsertMany(state,payload)
state.loading = false
})
},})
export default tripSlice.reducer
从await findOne(uid)
返回的API响应
{
created_by: "6040c2d1-ea57-43b6-b5f2-58e84b220f4e",deleted_by: null,destination: "Valencia",end_date: "2020-10-04",start_date: "2020-09-27",trip_id: "34a620e8-51ff-4572-b466-a950a8ce1c8a",uid: "14047a5b-2fe5-46c9-b7f2-e9b5d14db05b",updated_by: null,trip_items: [
{
destination: "mezzanine Level Shivaji Stadium Metro Station,Baba Kharak Singh Rd,Hanuman Road Area,Connaught Place,New Delhi,Delhi 110001,India",end_date: "2020-09-28",end_time: "2020-09-28T01:20:15.906Z",note: null,start_date: "2020-09-28",start_time: "2020-09-28T01:20:15.906Z",trip_item_id: "bd775be7-2129-42c0-a231-5a568b0f565d",trips_items_members: [
{
trip_item_member_id: "76b54a80-4d09-4768-bc5a-4d7e153e66dc",uid: "4b88f9af-8639-4bb0-93fa-96fe97e03d02",}
],uid: "e5f81a6d-1a0d-4456-9d4e-579e80bc27d8",}
],trips_members: [
{
trip_member_id: "76b54a80-4d09-4768-bc5a-4d7e153e66dc",role: "ADMIN"
}
]
}
这是我的normalizr模式
const tripItemmember = new schema.Entity(
'trips_items_members',{},{ idAttribute: 'trip_item_member_id' },)
const tripItem = new schema.Entity(
'trips_items',{
trips_items_members: [tripItemmember],},{
idAttribute: 'trip_item_id',)
const tripMember = new schema.Entity(
'trips_members',{
idAttribute: 'trip_member_id',)
export const trip = new schema.Entity(
'trip',{
trips_items: [tripItem],trips_members: [tripMember],{
idAttribute: 'trip_id',)
trip: {
"34a620e8-51ff-4572-b466-a950a8ce1c8a": {
created_by: "6040c2d1-ea57-43b6-b5f2-58e84b220f4e"
deleted_by: null
destination: "Valencia"
end_date: "2020-10-04"
start_date: "2020-09-27"
trip_id: "34a620e8-51ff-4572-b466-a950a8ce1c8a"
trips_items: ["bd775be7-2129-42c0-a231-5a568b0f565d"]
trips_members: ["76b54a80-4d09-4768-bc5a-4d7e153e66dc"]
uid: "14047a5b-2fe5-46c9-b7f2-e9b5d14db05b"
updated_by: null
}
}
trips_items:{
"0a56da0f-f13b-4c3d-896d-30bccbe48a5a": {
destination: "mezzanine Level Shivaji Stadium Metro Station"
end_date: "2020-09-28"
end_time: "2020-09-28T01:20:15.906Z"
note: null
start_date: "2020-09-28"
start_time: "2020-09-28T01:20:15.906Z"
trip_item_id: "0a56da0f-f13b-4c3d-896d-30bccbe48a5a"
trips_items_members: []
uid: "25d20a9d-1eb9-4226-926d-4d743aa9d5dc"
}
}
trips_members: {
"76b54a80-4d09-4768-bc5a-4d7e153e66dc": {
role: "ADMIN"
trip_member_id: "76b54a80-4d09-4768-bc5a-4d7e153e66dc"
uid: "4b88f9af-8639-4bb0-93fa-96fe97e03d02"
}
}
解决方法
您的设置非常类似于redux-toolkit文档中的this detailed example。他们正在获取articles
,但是每篇文章都带有嵌入的users
和comments
。它们为三个实体中的每个实体定义单独的切片。
comments
切片没有任何动作或归约方法,但使用extraReducers
属性来响应文章收到的动作并存储嵌入的注释。
const commentsAdapter = createEntityAdapter();
export const slice = createSlice({
name: "comments",initialState: commentsAdapter.getInitialState(),reducers: {},extraReducers: {
[fetchArticle.fulfilled]: (state,action) => {
commentsAdapter.upsertMany(state,action.payload.comments);
}
}
});
fetchArticle
动作由article
片“拥有”,但是动作有效负载包含所有三种类型的实体。 所有切片均接收所有操作,因此comments
和users
能够以自己的逻辑来响应此操作。每个切片都不会影响其他切片的工作。
在您的情况下,您想为items
和members
创建切片。您希望不按实体类型来键入有效负载,而不用调用upsertMany(state,payload)
。这样您就可以调用upsertMany(state,payload.members)
。