Using ngrx/redux, and following the ngrx example app as close as possible. Now I'm doing this somewhere in the application:
get totalItems (): number {
return sumBy(this._data.items, 'metadata.value');
}
Where the _data
is the raw async data from a remote server, and is part of some data of a User
.
Now I have some options:
1. Create a Class User
constructor (public _data: UserResponse) {}
And have some methods like:
get name (): string {
return this._data.name;
}
get address (): string {
return this._data.address;
}
And additionally things like the aforementioned totalItems
, and other shortcuts to get a different result than the raw data.
What I don't like is the repetition of the properties, and where to use this class;
- Do I use the
User
Class in theapi-service
, and return the transformed data to the the@Effect
module, which in turn will trigger theSUCCESS
orFAILED
actions? - Do I put it in the
@Effect
module, right before the action triggers?- Do I use
extends
orimplements
on the angular component?
- Do I use
Another thing is that we apply the transformed data on the payload
property, which would make it hard to debug the raw remote data.
2. Another option would be to transform this data inside the reducer
:
The current reducer:
case ActionTypes.FETCH_USERS_SUCCESS:
return {
...state,
users: action.payload
};
What I could do:
case ActionTypes.FETCH_USERS_SUCCESS:
return {
...state,
users: action.payload.map((user) => {
return({
...user,
totalItems: sumBy(user.items, 'metadata.value' });
})
};
Is this an anti-pattern or bad practice? Because I see .filter
being used in examples all the time, and I don't see summing up as being impure, so what would be the arguments for/against doing it in the reducer? This way I could eliminate needing a User
class, and load all the necessary data into a component.
And, keeping the reducers from filling up with a lot of logic, perhaps make a separate reducer for this, which "listens" to the same actions.
Option 3:
Put it in a selector:
export const selectUsers = createSelector(selectUsersState, (state: Userstate) => state.payload.map((a: UsersResponse) => new User(a)))
Reading this answer by @dan_abramov: How to put methods onto the objects in Redux state? implies me that the 2nd option makes more sense for this, as he states:
"In Redux, you don't really have custom models. Your state should be plain objects (or Immutable records). They are not expected to have any custom methods."