In my Angular2 application, I use ngrx to manage states, so when I receive data from the server, I dispatch an action to a reducer.
MyExampleReducer.ts :
export const Reviews: ActionReducer<any> = (state: any[] = [], action: Action) => {
switch (action.type) {
case GET_REVIEWS:
return action.payload;
case ADD_REVIEW :
return [...state, {review : action.payload, replays : []}];
case UPDATE_REVIEW:
return '' // return what ?
case DELETE_REVIEW:
return state.filter(item => {
return item.id !== action.payload.id;
});
default:
return state;
}
};
The problem is when i have to update an item in my Reviews array, what is the best way to do in the redux way ?
First, we will find the index of the item in the array using findIndex() . Then we make a copy of the todos array from the state. Then we can change the value in the new array using the index value we got from findIndex() . Lastly, in the return, we reassign todos to that new array.
Reducers in NgRx are responsible for handling transitions from one state to the next state in your application. Reducer functions handle these transitions by determining which actions to handle based on the type.
Redux: Update an Object When you want to update the top-level properties in the Redux state object, copy the existing state with ... state and then list out the properties you want to change, with their new values.
This is a normal convention with NgRx. The payload is what the store is going to update itself to be. If the payload only consists of one value, it's often convention to not include a payload wrapper and just return the value (i.e. action. locationData instead of action. payload.
You can use map
to return an array that has the element that corresponds to the action updated:
export const Reviews: ActionReducer<any> = (state: any[] = [], action: Action) => {
switch (action.type) {
case ADD_REVIEW:
return [...state, { review: action.payload, replays: [] }];
case UPDATE_REVIEW:
return state.map(item => item.id === action.payload.id ? { review: action.payload, replays: [] } : item);
case DELETE_REVIEW:
return state.filter(item => item.id !== action.payload.id);
default:
return state;
}
}
Also, you can simplify the reviews reducer by using the review reducer to perform the ADD_REVIEW
and UPDATE_REVIEW
actions - the reviews reducer is then only concerned with managing the list of reviews and not the reviews themselves:
import { reviewReducer } from '...';
export const Reviews: ActionReducer<any> = (state: any[] = [], action: Action) => {
switch (action.type) {
case ADD_REVIEW:
return [...state, reviewReducer(undefined, action)];
case UPDATE_REVIEW:
return state.map(item => item.id === action.payload.id ? reviewReducer(item, action) : item);
case DELETE_REVIEW:
return state.filter(item => item.id !== action.payload.id);
default:
return state;
}
}
Assuming that your state is just an array full of reviews you could do the following:
export const Reviews: ActionReducer<any> = (state: any[] = [], action: Action) => {
switch (action.type) {
case GET_REVIEWS:
return action.payload;
case ADD_REVIEW :
return [...state, {review : action.payload, replays : []}];
case UPDATE_REVIEW:
// get an array of all ids and find the index of the required review
let index = state.map(review => review.id)
.indexOf(action.payload.id);
return [
...state.slice(0, index),
Object.assign({}, state[index], action.payload),
...state.slice(index + 1)
]
case DELETE_REVIEW:
return state.filter(item => {
return item.id !== action.payload.id;
});
default:
return state;
}
First you need to find the index of the review that should be updated. After that you can create a new array where you replace the object at the index's position.
A great resource for this kind of mutations is this video.
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