The data I get from the remove API is not in a format that my app can handle. My saga downloads the data.
Who should handle the normalization?
The saga itself before dispatching the success action with the normalized data?
Or should the router normalize the date before building the new state?
Edit I opted to normalize in the saga and keep the reducer clean. It just replaces the activities with the new ones activitiesUpdated
gives it.
The reducer
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
}
}
And those are the sagas:
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)
}
}
}
When you think about the double wrapped while loops in the updateActivities
saga?
Also is it correct that
yield take([REFRESH_ACTIVITIES, LOGOUT])
is just a shortcut for
yield race[take(REFRESH_ACTIVITIES), take(LOGOUT)]
Ultimately you're free to do whatever works for you in this case--there isn't a strong case for one over the other. You may end up finding, depending on how the data is structured, that doing it in the saga will have less code because you're only taking apart the result once vs twice (once in each of 2 reducers that care about the data. But this may or may not be the case. I also like the idea of doing it in the reducer because reducers should typically be as simple as possible, and this model fits with that.
But like I said, I don't think there's a strong generalized argument for one over the other.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With