在 redux-saga 或 reducer 中处理下载的项目规范化?
Handle downloaded item normalization in redux-saga or reducer?
我从删除 API 中获得的数据不是我的应用程序可以处理的格式。
我的 saga 下载数据。
谁应该处理规范化?
在使用规范化数据调度成功操作之前的 saga 本身?
或者路由器应该在建立新状态之前规范化日期吗?
编辑 我选择在 saga 中规范化并保持 reducer 清洁。它只是用 activitiesUpdated
提供的新活动替换活动。
减速器
export default function account(state = ACCOUNT, action) {
switch (action.type) {
case "account/LOGIN_SUCCESS":
const { access_token, user } = action
return { ...state, user, access_token, authenticated: true, error: "" }
case "account/LOGOUT_SUCCESS":
return ACCOUNT
case "account/LOGIN_ERROR":
return { ...state, error: action.error }
case "account/ACTIVITIES_UPDATED":
return { ...state, activities: action.activities }
default:
return state
}
}
那些是传奇故事:
function sortActivities(activities) {
return action.activities.sort((a,b) => b.timestamp.localeCompare(a.timestamp))
}
function addInvoices(activities) {
let lastYearMonth, invoiceItem
return activities.reduce((state, item, index) => {
const currentYearMonth = item.timestamp.substr(0,7)
if (currentYearMonth != lastYearMonth) {
lastYearMonth = currentYearMonth
invoiceItem = {
id: currentYearMonth,
type: "invoice",
parking: 0,
rebates: 0,
sum: 0,
timestamp: currentYearMonth
}
state.push(invoiceItem)
}
const amount = Math.abs(Number(item.gross_amount))
if (item.type == "parking") {
invoiceItem.parking += amount
invoiceItem.sum -= amount
} else if (item.type == "rebate" || item.type == "surplus") {
invoiceItem.rebates += amount
invoiceItem.sum += amount
}
state.push(item)
return state
}, [])
}
function *getActivities(access_token) {
console.info("fetch activities")
try {
const activities = yield call(getActivitiesAsync, access_token)
console.info("activities fetched")
yield put(activitiesUpdated(addInvoices(activities.sortActivities(activities))))
} catch (error) {
}
}
function *updateActivities() {
while (true) {
const { access_token } = yield take(LOGIN_SUCCESS)
console.info("Calling getActivities")
yield call(getActivities, access_token)
while (true) {
const {type } = yield take([REFRESH_ACTIVITIES, LOGOUT])
if (type == LOGOUT) {
break
}
yield call(getActivities, access_token)
}
}
}
当您想到 updateActivities
传奇中的双重 while 循环时?
另外
是否正确
yield take([REFRESH_ACTIVITIES, LOGOUT])
只是
的快捷方式
yield race[take(REFRESH_ACTIVITIES), take(LOGOUT)]
最终,在这种情况下,您可以自由地做任何对您有用的事情——没有一个比另一个更有说服力的理由。根据数据的结构,您最终可能会发现,在 saga 中执行此操作的代码会更少,因为您只将结果分开一次而不是两次(在关心数据的 2 个减速器中的每一个中一次)。但是这可能是也可能不是这种情况。我也喜欢在减速器中这样做的想法,因为减速器通常应该尽可能简单,而这个模型符合这一点。
但就像我说的,我认为没有一个强有力的普遍论据支持另一个。
您还可以使用 createSelector 进行数据规范化,以保持 sagas 清洁:
- Selectors can compute derived data, allowing Redux to store the minimal possible state.
- Selectors are efficient. A selector is not recomputed unless one of its arguments changes.
- Selectors are composable. They can be used as input to other selectors.
我从删除 API 中获得的数据不是我的应用程序可以处理的格式。 我的 saga 下载数据。
谁应该处理规范化?
在使用规范化数据调度成功操作之前的 saga 本身?
或者路由器应该在建立新状态之前规范化日期吗?
编辑 我选择在 saga 中规范化并保持 reducer 清洁。它只是用 activitiesUpdated
提供的新活动替换活动。
减速器
export default function account(state = ACCOUNT, action) {
switch (action.type) {
case "account/LOGIN_SUCCESS":
const { access_token, user } = action
return { ...state, user, access_token, authenticated: true, error: "" }
case "account/LOGOUT_SUCCESS":
return ACCOUNT
case "account/LOGIN_ERROR":
return { ...state, error: action.error }
case "account/ACTIVITIES_UPDATED":
return { ...state, activities: action.activities }
default:
return state
}
}
那些是传奇故事:
function sortActivities(activities) {
return action.activities.sort((a,b) => b.timestamp.localeCompare(a.timestamp))
}
function addInvoices(activities) {
let lastYearMonth, invoiceItem
return activities.reduce((state, item, index) => {
const currentYearMonth = item.timestamp.substr(0,7)
if (currentYearMonth != lastYearMonth) {
lastYearMonth = currentYearMonth
invoiceItem = {
id: currentYearMonth,
type: "invoice",
parking: 0,
rebates: 0,
sum: 0,
timestamp: currentYearMonth
}
state.push(invoiceItem)
}
const amount = Math.abs(Number(item.gross_amount))
if (item.type == "parking") {
invoiceItem.parking += amount
invoiceItem.sum -= amount
} else if (item.type == "rebate" || item.type == "surplus") {
invoiceItem.rebates += amount
invoiceItem.sum += amount
}
state.push(item)
return state
}, [])
}
function *getActivities(access_token) {
console.info("fetch activities")
try {
const activities = yield call(getActivitiesAsync, access_token)
console.info("activities fetched")
yield put(activitiesUpdated(addInvoices(activities.sortActivities(activities))))
} catch (error) {
}
}
function *updateActivities() {
while (true) {
const { access_token } = yield take(LOGIN_SUCCESS)
console.info("Calling getActivities")
yield call(getActivities, access_token)
while (true) {
const {type } = yield take([REFRESH_ACTIVITIES, LOGOUT])
if (type == LOGOUT) {
break
}
yield call(getActivities, access_token)
}
}
}
当您想到 updateActivities
传奇中的双重 while 循环时?
另外
是否正确yield take([REFRESH_ACTIVITIES, LOGOUT])
只是
的快捷方式yield race[take(REFRESH_ACTIVITIES), take(LOGOUT)]
最终,在这种情况下,您可以自由地做任何对您有用的事情——没有一个比另一个更有说服力的理由。根据数据的结构,您最终可能会发现,在 saga 中执行此操作的代码会更少,因为您只将结果分开一次而不是两次(在关心数据的 2 个减速器中的每一个中一次)。但是这可能是也可能不是这种情况。我也喜欢在减速器中这样做的想法,因为减速器通常应该尽可能简单,而这个模型符合这一点。
但就像我说的,我认为没有一个强有力的普遍论据支持另一个。
您还可以使用 createSelector 进行数据规范化,以保持 sagas 清洁:
- Selectors can compute derived data, allowing Redux to store the minimal possible state.
- Selectors are efficient. A selector is not recomputed unless one of its arguments changes.
- Selectors are composable. They can be used as input to other selectors.