#redux #normalizr #redux-toolkit
#redux #normalizr #redux-инструментарий
Вопрос:
У меня есть глубоко вложенный объект данных, который возвращается из моего API, который выглядит как приведенный ниже JSON.
Я использую инструментарий Redux createSlice
для создания фрагмента trip
Итак, в настоящее createSlice
время я хочу сохранить массив поездок.
Я также хочу иметь возможность обновлять одну поездку или часть поездки
- например, допустим, я хочу обновить дату начала элемента поездки
- или, допустим, я хочу обновить имя участника элемента trip
Мои вопросы и проблемы:
- В настоящее время все эти объекты возвращаются в
trip
createSlice, но я не уверен, следует ли после нормализации объектов разделять их на отдельныеcreateSlice
s? если да, то как это делается или это анти-шаблон? - как следует определять вложенные объекты в
initialState
? - должен ли я определить все мои нормализованные объекты в моем
initalState
? если я это сделаю, как будут выглядеть мои редукторы, когда я захочу обновитьtrip_item
илиtrip_item_member
? - мои нормализованные данные даже выглядят «правильными»? Я опустил использование
mergeStrategy
betweentrips_items_members
иtrip_members
что, как я знаю, я должен сделать, но еще не понял, как это работает, или если это необходимо здесь?
Примечание:
В документах RTK здесь есть пример, который показывает 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, (state, { payload }) => {
console.log('payload', payload)
tripsAdapter.upsertMany(state, payload)
state.loading = false
})
},
})
export default tripSlice.reducer
Ответ API, который возвращается из await findOne(uid)
{
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",
uid: "4b88f9af-8639-4bb0-93fa-96fe97e03d02",
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',
},
)
Это вывод из normalizr
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"
}
}
Ответ №1:
Ваша настройка очень похожа на этот подробный пример из документации redux-toolkit. Они извлекаются 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)
.