How to destruct object in reducer (REACT) - reactjs

I have the following userData in state
userData : {
isValidCheckup: true,
accounts: {
userAccount: [
{
accountType: 'checkings',
includeInCheckup: false
},
{
accountType: 'checkings',
includeInCheckup: false
}
]
}
Now, I have a reducer to handle manipulation of above data. Note: I only want to update includeInCheckup field. Depending on the accountType recieved as a payload to the reducer.
Here is the reducer function I am using
case UPDATE_USER_DATA: {
const { field, value } = action.payload; // field='checkings', value=true
return {
...state,
userData: {
...state.userData
}
}
}
As you can see, I am getting field='checkings', value=true from the payload to the reducer. How can I specifically update this value to the includeInCheckup property based on the field value.
Can someone please shed some light.

When I get field as 'checkings', I update the first element of array
'userAccount' and when I get field as 'savings, I will update the
second element of array.
For example state, I am assuming userAccount[0] is a checking account type, and userAccount[1] is a savings account type, and the array will always be length 2.
userData : {
isValidCheckup: true,
accounts: {
userAccount: [
{
accountType: 'checkings',
includeInCheckup: false
},
{
accountType: 'savings',
includeInCheckup: false
}
]
}
Copy each level of state that is being updated, and then update the property by creating a new object/array reference. You can simply map the userAccount array and check if the account type matches that of the action payload field, and update the includeInCheckup property.
case UPDATE_USER_DATA: {
const { field, value } = action.payload; // field='checkings', value=true
return {
...state,
userData: {
...state.userData,
accounts: {
...state.userData.accounts,
userAccount: state.userData.accounts.map((account) =>
account.accountType === field
? {
...account,
includeInCheckup: value
}
: account
)
}
}
};
}

Related

Redux state return empty state alternatively

I am getting filteredStudents state empty array alternatively even when clicking on a section, it returns an array of students but when I click on a different section, it returns an empty array. When I click again on the same section its value is updated. Is there any fix for this?
let filteredStudents=state.?filteredStudentsReducer?.filteredStudents
// dispatching action from main js value: {section: "A" } students:[{name:"a", division:"A" }, ] I am passing values similar to these.
dispatch({
type: "FILTER_STUDENTS_SECTION",
value: obj,
students: students,
});
// this is the reducer , to filter students based on section
case "FILTER_STUDENTS_SECTION":
//filter students based on section
let obj1 = action.value;
let stds1;
if (state.filteredStudents.length) {
stds1 = state.filteredStudents.filter((s) => {
return s.division === obj1.section;
});
} else {
stds1 = action?.students;
stds1 = stds1.filter((s) => {
return s.division === obj1.section;
});
}
return {
...state,
filteredStudents: stds1,
message: "Students filtered section wise",
loading: false,
};

Why the new value is not dispatched?

I am trying to set new values by redux but a single key "status" every time after dispatching as an empty string. I don't know what is it and why every time empty maybe somebody know solution?
call action and dispatch code:
store.dispatch(actionSetNewResponseFilterValues({
active_level: "1",
query_in: ["0"],
status: ["15"]})
);
action method:
export const actionSetNewResponseFilterValues = (newValues) => {
return {
type: SET_RESP_FILT_VALS,
newValues
}
}
and code in reducer:
case SET_RESP_FILT_VALS:
case LOAD_RESP_FILT_VALS:
return {
...state,
filter: {
...state.filter,
newValues: {
...state.filter.newValues,
...action.newValues
}
}
}
#udpated
when in reducer coding whithout destructuring-assignment:
case SET_RESP_FILT_VALS:
case LOAD_RESP_FILT_VALS:
return {
...state,
filter: {
...state.filter,
newValues: action.newValues
}
}
in dispatching I see an empty status key:

Delete multiple item from array - Redux State

I'm working on react app with redux. I want to delete multiple item from array. I write below code in my reducer which delete single item from array but i want to delete multiple item.
case DELETE_LINK:
let dltLink = state.filter(item => {
return item._id !== action.data._id
})
return {
...state,
parentFolderlinks: dltLink
};
It seems you want to filter links from state.parentFolderlinks, say you have the ids in action.data.ids, you could
case DELETE_LINK:
const parentFolderlinks = state.parentFolderlinks.filter(item => {
return !action.data.ids.includes(item._id);
});
return {
...state,
parentFolderlinks
};
On what basis would you like to filter items? I assume that multiple items will not have the same id.
Below example shows how we can filter multiple items in redux. In this case, foods state with items that has type as fruit and removes everything else.
// initial state with all types of foods
const initialState = {
"foods": [
{
name: "apple",
type: "fruit"
},
{
name: "orange",
type: "fruit"
},
{
name: "broccoli",
type: "vegetable"
},
{
name: "spinach",
type: "vegetable"
},
]
}
// sample reducer that shows how to delete multiple items
export default (state = initialState, { type, payload }) => {
switch (type) {
// delete multiple items that does not have type fruit
// i.e both brocolli and spinach are removed because they have type vegetable
case DELETE_ITEMS_WITHOUT_TYPE_FRUIT:
const onlyFruits = state.foods.filter(food => food.type === "fruit");
return {
...state,
foods: onlyFruits
}
}
}
you could map over the state and run it through a function that works out if you want to keep it or not (I don't know what your logic is for that) then return the array at the end
const keepThisItem =(item) => {
return item.keep
}
case DELETE_LINK:
let itemsToKeep = []
let dltLink = state.map(item => {
if(keepThisItem(item){
itemsToKeep.push(item)
}
return itemsToKeep
})

Updating a single value for each item in object in Redux State

I have a list of notifications inside redux state. As you can see below.
There could be more items in the list, and my goal is to set isRead value to true for all items in the list depending on the isDiscussionType value. I am using the following code in the reducer:
case MARKALLASREAD_NOTIFICATIONS_SUCCESS:
return {
...state,
loading: false,
notifications:
Object.keys(state.notifications).map(id => {
if (state.notifications[id].isDiscussionType == action.payload.isDiscussionType)
return { ...state.notifications[id], isRead: true }
else
return { ...state.notifications[id] }
})
};
This code results in the following state, where the key is lost (set to 0), and the isRead value is not changed (although the database is updated correctly).
Do you see any problems in the code I shared above?
Map returns an array instead of an object. That is why you lose your id, since the 0 is just the index within the array, which is now under notifications. I would move the generation of notifications out of the return to get more flexibility:
case MARKALLASREAD_NOTIFICATIONS_SUCCESS:
const notifications = { ...state.notifications }
Object.values(notifications).forEach(notification => {
if(notification.isDiscussionType === action.payload.isDiscussionType) {
notifications[notification.id] { ...notification, isRead: true }
}
}
return {
...state,
loading: false,
notifications
};
This will return an immutable object with each notification changed, if the isDiscussionType are the same. Because map, filter, reduce return arrays, I would move it out of the return and use forEach.
Hope this helps. Happy coding.
If your notifications is a list i.e. an array, you shouldn't use Object.keys on it, and go straight with the map:
case MARKALLASREAD_NOTIFICATIONS_SUCCESS:
return {
...state,
loading: false,
notifications: state.notifications.map(notification => {
if (notification.isDiscussionType === action.payload.isDiscussionType) {
return {
...notification,
isRead: true
}
} else {
return notification;
}
})
};

React/Redux updating a certain value in an array of objects

I am just learning redux and this is my first time using it in a project. I am trying to update a certain value in an array of objects. The structure of my object is:
students: {
loading: false,
error: null,
data: [{
id: 1,
name: "Bob",
email: 'whatever#gmail.com',
status: 'out'
}]
}
Below are my actions for this and the data it gets back is the id of the student that it needs to update. These work fine.
export const studentCheckInStart = student => ({
type: "STUDENT_CHECK_IN_START",
student
})
export const studentCheckIn = (id) => {
return dispatch => {
dispatch(studentCheckInStart())
return axios.put('http://localhost:8080/studentList/'+id)
.then((response) => {
dispatch(studentCheckInSuccess(response.data))
}).catch(err => {
dispatch(studentCheckInError(err))
})
}
}
export const studentCheckInSuccess = (data) => {
return {
type: STUDENT_CHECK_IN_SUCCESS,
payload: data
}
}
export const studentCheckInError = (error) => {
return {
type: STUDENT_CHECK_IN_ERROR,
error
}
}
Where I'm having the issue is in the reducer
case "STUDENT_CHECK_IN_SUCCESS":
let updatedStudent = state.students.data.findIndex((student) => {
return student.id === action.payload
})
console.log(updatedStudent)
return {
...state,
students: {
...state.students[updatedStudent],
data: {
status:'in'
}
}
};
break;
case "STUDENT_CHECK_IN_START":
return {
...state,
students: {
...state.students,
loading: true
}
}
break;
case "STUDENT_CHECK_IN_ERROR":
return {
...state,
students: {
...state.students,
error: action.payload,
loading: false
}
}
break;
I'm trying to target the specific student object using the id to find the index of the student I want to target. Then change just the status of that object to "in". I know what I have in the STUDENT_CHECK_IN_SUCCESS is incorrect, I'm just not sure how to do it.
Your state seems a little bit complex. Why do you need loading or error in your students object? What other parts do you have in your state beside students? This is one possible way I can think of at this situation (just the related part) :
let updatedStudent = state.students.data.findIndex(
student => student.id === action.payload
);
const newData = [ ...state.students.data ];
newData[ updatedStudent ] = { ...newData[ updatedStudent ], status: "in" }
return { ...state, students: { ...state.students, data: newData } };
I will edit my answer if I think a better way.
It looks like your action doesn't really need all that payload, just an id of the student who checked in. So if you change that, I think you could return this from your reducer action:
return {
...state,
students: {
...state.students,
data: state.students.data.map(s => {
if (s.id === action.id) {
return { ...s, status: 'in' };
}
return s;
}
}
};
The idea is that you need to return everything unchanged except the data array. By using map, we can return a modified version of the data array where the student whose id matches the one supplied in the action will have their status changed to in, but the rest of the students in the data array remain unchanged.

Resources