change state variable (array of objects) in react - reactjs

I am very new to react and have a very straightforward usecase.
on a certain function call, I need to update one of the state variables - which is an array of objects.
I need to iterate through this array find an element and add a key the object in that element.
I tried this way but its not working.
const [finalStudents, setFinalStudents] = useState([]);
function setAttentionForStudent(deviceName, value) {
// console.log("Device ID:", deviceName)
// console.log("Attention value:", value)
finalStudents.map((student, index) => {
console.log("student", student)
if (student['device']['deviceName'] == deviceName) {
console.log("student inside", student)
setFinalStudents((prevFinalStudents) => {
console.log("prev final student",prevFinalStudents)
prevFinalStudents[index]['device']['attentionValue'] = value
})
// student['device']['attentionValue'] = value
} else {
setFinalStudents((prevFinalStudents) => {
prevFinalStudents[index]['device']['attentionValue'] = 0
})
}
})
// console.log(finalStudents)
}

Try this:
const [finalStudents, setFinalStudents] = [];
const setAttentionForStudent = (deviceName, value) => {
if (finalStudents.length !== 0) {
for (var x = 0; x < finalStudents.length; x++) {
if (finalStudents[x].device.deviceName === deviceName) {
finalStudents[x].device.deviceName = value;
setFinalStudents(new Array(...finalStudents));
} else {
finalStudents[x].device.deviceName = value;
setFinalStudents(new Array(...finalStudents));
}
}
}
};

callback in setFinalStudents should return an array to update state. You can use map in setFinalStudents like this:
setFinalStudents((prevFinalStudents) => {
return prevFinalStudents.map((student) => {
if (student["device"]["deviceName"] == deviceName) {
student["device"]["attentionValue"] = value;
} else {
student["device"]["attentionValue"] = value;
}
return student;
});
});

Was finally able to solve the problem by the following way:
setFinalStudents((prevFinalStudents) => {
const clonedFinalStudents = [...prevFinalStudents];
return clonedFinalStudents.map((student) => {
let updatedStudent = { ...student };
let attentionValue = 0;
if (student['device']['deviceName'] == deviceName) {
attentionValue = value;
}
updatedStudent = {
...updatedStudent,
device: {
...updatedStudent.device,
attentionValue,
},
};
return updatedStudent;
});
});

Related

Why my const var is empty but the related filter works

RemoveArea(area: models.Area) {
let messages: string[] = [];
messages = messages.concat(this.getMessagesFromDeletingArea(area));
this._translate.get('QuestionGroup_RemoveAreaConfirm',
{ thisCaption: area.caption[this.BlueContext.currentLanguage] }).subscribe(nextCaption => {
this.dialogsService.confirm(nextCaption, messages)
.subscribe(confirmed => {
if (confirmed) {
this._areaService.removeArea(this.dashboardConfiguration, area, this.BlueContext.currentLanguage);
const index = this.areas.findIndex(a => a.id === area.id);
if (index > -1) {
this.areas.splice(index, 1);
//here
this.dashboardConfiguration.widgets.forEach(wAId => {
const allWidgetByAreaId = wAId.widgets.filter(w => w.areaId === area.id);
allWidgetByAreaId.forEach(w => {
w.areaId = null;
});
});
}
}
});
});
}
The filter is working but the const var (allWidgetByAreaId) is undefined and empty so "for each " does not work. Would you please help?

React native, multiple records getting pushed after 2nd object being pushed onto the array

React Native, Im trying to check the whether the object is present in the array or not, if present Im replacing the object with the new one, but after the 2nd object is inserted 3rd object is pushed twice, 4th object 4 times, Im not understanding why this behaviour is happening, is it because of immutable array or what?
useEffect(() => {
if (route.params && route.params.lookup_scan) {
showShipments();
}
}, [route]);
const showShipments = () => {
if (SegregationContext.shipments.length > 0) {
SegregationContext.shipments.forEach((item, key: number) => {
if (item.lookup_scan === route.params.lookup_scan) {
SegregationContext.shipments[key] = route.params;
} else {
SegregationContext.shipments.push({ ...route.params });
}
});
} else {
SegregationContext.shipments.push(route.params);
}
setShipments([...SegregationContext.shipments]);
setIsLoader(false);
};
what the solution was:
const index = shipments.findIndex(i => i.lookup_scan === newobj.lookup_scan); // Returns 2.
if (index === -1) {
shipments.push(newobj);
} else {
shipments[index] = newobj;
}
This is what I did but it didnt seem to work bcz it added the object once again after finishing the loop:
if (shipments.length > 0) {
shipments.map((item, key: number) => {
if (item.lookup_scan === newobj.lookup_scan) {
shipments[key] = newobj;
}
});
shipments.push(newobj);
} else {
shipments.push(newobj);
}
This is what I was doing:
if (shipments.length > 0) {
shipments.map((item, key: number) => {
if (item.lookup_scan === newobj.lookup_scan) {
shipments[key] = newobj;
} else {
shipments.push(newobj);
}
});
} else {
shipments.push(newobj);
}
actual code:
if (SegregationContext.shipments.length > 0) {
SegregationContext.shipments.forEach((item, key: number) => {
if (item.lookup_scan === route.params.lookup_scan) {
SegregationContext.shipments[key] = route.params;
}
});
SegregationContext.shipments.push({ ...route.params });
} else {
SegregationContext.shipments.push(route.params);
}
setShipments([...SegregationContext.shipments]);

Need helping know exactly what redux-toolkit is doing in the reducer method

I am currently creating a cart for a website that has multiple parts to it, this is what it looks like:
cart: {
dozenBoxes: [{dozen: 10.50, boxes:[[]]}, {dozen: 2.16, boxes:[[]]}],
dozenableDonuts: [{dozen: 10.50, donuts: []}, {dozen: 2.16, donuts: []}],
specialDonuts: []
}
The first route I took when creating this cart was redux-toolkit. I quickly realized that I didn't fully understand how redux worked because when trying to manipulate the nested parts of the cart. I was able to somewhat figure it out using the spread operator, but that only got me so far.
Here is what my cart reducer looked like:
const cartSlice2 = createSlice({
name: "cart2",
initialState,
reducers: {
addDonut(state, action) {
let { dozenableDonuts, specialDonuts, dozenBoxes } = state;
const { item, type } = action.payload;
console.log(current(dozenableDonuts));
if (type === "dozenable") {
let { dozenableDonutsNew, index } = addDonutToCart(
[...dozenableDonuts],
item
);
console.log(dozenableDonutsNew);
if (dozenableDonutsNew[index].amount >= 12) {
let { dozenBoxesNew, dozenableDonutsFinal } = createDozenBoxes(
[...dozenBoxes],
{ ...dozenableDonutsNew[index] }
);
state.dozenBoxes = dozenBoxesNew;
state.dozenableDonuts[index] = dozenableDonutsFinal;
} else {
state.dozenableDonuts = dozenableDonutsNew;
}
} else if (type === "special") {
specialDonuts = addSpecialToCart([...state.specialDonuts], item);
}
console.log(current(state));
},
},
});
I was able to get into the "createDozenBoxes" method but was not able to update the state correctly after, I also got the error: "TypeError: Cannot perform 'IsArray' on a proxy that has been revoked reduxtoolkit" where I dispatched the action.
I have tried to look up how to deal with nested state with redux, but I haven't found anything. How do you deal with nested state while using redux?
Methods:
export function addDonutToCart(dozenableDonuts, donut) {
const { name, extras, pricePer, amount, dozen } = donut;
const newDonut = {
name,
extras,
pricePer,
amount,
totalDonutPrice: getTotaldonutPrice(amount, pricePer),
dozen,
};
let index = 0;
//Checking if this donut's dozen group already exists
const dozenIndex = dozenableDonuts.findIndex(
(donutInfo) => donutInfo.dozen === dozen
);
if (dozenIndex !== -1) {
if (amount > 12) {
index = dozenableDonuts.length;
} else {
index = dozenIndex;
}
console.log(`Adding to $${dozen} Dozen Group!`);
let { donuts } = dozenableDonuts[dozenIndex];
const donutItemIndex = donuts.findIndex(
(donut) => donut.name === name && compareObjects(donut.extras, extras)
);
if (donutItemIndex === -1) {
console.log("Adding New donut!");
donuts.push(newDonut);
dozenableDonuts[dozenIndex].amount += amount;
} else {
console.log("Found donut, Adding to Amount!");
donuts[donutItemIndex].amount += newDonut.amount;
donuts[donutItemIndex].totalDonutPrice += newDonut.totalDonutPrice;
dozenableDonuts[dozenIndex].amount += amount;
}
//Update the given dozenableDonuts array to have the updated donuts
dozenableDonuts[dozenIndex].donuts = donuts;
} else {
console.log("Added NEW Dozen Group!");
dozenableDonuts.push({ dozen, donuts: [newDonut], amount });
}
// console.log(`Dozenable donuts`);
// console.log(dozenableDonuts);
return { dozenableDonutsNew: dozenableDonuts, index };
}
export function createDozenBoxes(dozenBoxes, dozenableDonuts) {
let { dozen, donuts, amount } = dozenableDonuts;
const numBoxes = Math.floor(amount / 12);
let newDozenBoxes;
const dozenIndex = dozenBoxes.findIndex((dozenGroup) => dozenGroup === dozen);
if (dozenIndex !== -1) {
console.log(`Found the $${dozen} Category!`);
newDozenBoxes = dozenBoxes[dozenIndex].boxes;
} else {
console.log("Created new Dozen Category!");
dozenBoxes.push({ dozen, boxes: [] });
newDozenBoxes = [[]];
}
let amountTillFull = 12;
let currBox = 0;
let newDonuts = donuts
.map((donut) => {
if (currBox < numBoxes) {
let { amount } = donut;
if (amountTillFull > amount) {
newDozenBoxes[currBox].push(donut);
amountTillFull -= amount;
donut.amount = 0;
} else if (amountTillFull === amount) {
newDozenBoxes[currBox].push(donut);
amountTillFull = 12;
currBox++;
donut.amount = 0;
} else if (amountTillFull < amount) {
console.log("AmountTillFull is less than amount!");
let remainingDonuts = amount - amountTillFull;
newDozenBoxes[currBox].push({ ...donut, amount: amountTillFull });
amountTillFull = 12;
currBox++;
const dozensToMake = Math.floor(remainingDonuts / 12);
if (currBox < numBoxes) {
if (dozensToMake === 0) {
newDozenBoxes[currBox].push({
...donut,
amount: remainingDonuts,
});
amountTillFull -= remainingDonuts;
donut.amount = 0;
} else {
let newDonut = { ...donut, amount: 12 };
for (let i = 0; i < dozensToMake; i++) {
newDozenBoxes.push(newDonut);
remainingDonuts - 12;
currBox++;
}
if (currBox < numBoxes) {
newDozenBoxes[currBox].push({
...donut,
amount: remainingDonuts,
});
amountTillFull -= remainingDonuts;
donut.amount = 0;
} else {
donut.amount = remainingDonuts;
}
}
}
}
}
return donut;
})
.filter((donut) => donut.amount !== 0);
dozenBoxes.boxes = newDozenBoxes;
return { dozenBoxesNew: dozenBoxes, dozenableDonutsFinal: newDonuts };
}

Checking an array for existing array items passed as a parameter

I have 2 react functions below
const hasPermission = (permission: string) => {
if (user?.permissionsEnabled === false) {
return true
}
return permissions.includes(permission) || false
}
The function below accepts an array of a particular type
I want a situation where if any item in the array passed here(parameter) exist in the enum
UserPermissions I want to return a true.
return true does not seem to be working in the method below.
const hasAnyPermission = (permissionsPassed: UserPermissions[]) => {
permissionsPassed.map(permission => {
if (hasPermission(permission)) {
return true
}
})
return false
}
I am calling hasAnyPermission like this..
hasAnyPermission([Edit,View])
Array.prototype.map only returns an array, you need to use Array.prototype.some instead:
const hasAnyPermission = (permissionsPassed: UserPermissions[]) => {
return permissionsPassed.some(permission => {
if (hasPermission(permission)) {
return true
}
})
}
Which is equivolent to:
const hasAnyPermission = (permissionsPassed: UserPermissions[]) => {
return permissionsPassed.some(permission => hasPermission(permission));
}
Or use the regular for-of loops:
const hasAnyPermission = (permissionsPassed: UserPermissions[]) => {
for(let permission of permissionsPassed) {
if (hasPermission(permission)) {
return true
}
}
return false
}
Abbreviated fully-working example:
enum UserPermissions { Edit, View, Delete }
const user = { permissions: [UserPermissions.View] }
console.log("has permission?", hasAnyPermission(user, [UserPermissions.Delete]))
function hasAnyPermission(user, permissions: UserPermissions[]) {
if (user?.permissionsEnabled === false) return true
const userHasPermission = (p) => user.permissions.includes(p)
return permissions.some(userHasPermission)
}

React TypeError: " this is undefined ", function inside function call not working

Trying to call addChildNodeNext method in addChildNode function, but
result = this.addChildNodeNext(item.childrens,CurrentID)
gives error of this is undefined. I have already bind both function in constructor.
class TestAdd extends Component {
constructor(props) {
super(props)
this.addChildNode = this.addChildNode.bind(this)
this.addChildNodeNext = this.addChildNodeNext.bind(this)
}
addChildNodeNext = (nodeList, CurrentID) => {
alert(`Self ${nodeList} : ${CurrentID}`)
return nodeList
}
addChildNode = (nodeList, CurrentID) => {
const { childIndex } = this.state
let index = childIndex
const newTree = nodeList.filter(function (item) {
alert(`Self ${item.name} : ${CurrentID}`)
index += 1
let result = ""
if (item.name === CurrentID) {
const newName = `child_${childIndex}_${CurrentID}`
result = item.childrens.push({ name: newName, parent: newName, childrens: [] })
} else if (item.childrens.length > 0) {
result = this.addChildNodeNext(item.childrens, CurrentID)
} else {
result = item
}
return result
});
this.setState({ childIndex: index })
this.setState({ treeNode: newTree })
}
}
export default TestAdd;
You are using a regular function in your .filter method. This is why you lose this context there. Also, you don't need to bind your functions in the constructor because you are using arrow functions.
addChildNode = (nodeList, CurrentID) => {
const { childIndex } = this.state
let index = childIndex
const newTree = nodeList.filter(function (item) { // <--- HERE
alert(`Self ${item.name} : ${CurrentID}`)
index += 1
let result = ""
if (item.name === CurrentID) {
const newName = `child_${childIndex}_${CurrentID}`
result = item.childrens.push({ name: newName, parent: newName, childrens: [] })
} else if (item.childrens.length > 0) {
result = this.addChildNodeNext(item.childrens, CurrentID)
} else {
result = item
}
return result
});
this.setState({ childIndex: index })
this.setState({ treeNode: newTree })
}
You can replace it with an arrow function:
addChildNode = (nodeList, CurrentID) => {
const { childIndex } = this.state
let index = childIndex
const newTree = nodeList.filter(item => {
alert(`Self ${item.name} : ${CurrentID}`)
index += 1
let result = ""
if (item.name === CurrentID) {
const newName = `child_${childIndex}_${CurrentID}`
result = item.childrens.push({ name: newName, parent: newName, childrens: [] })
} else if (item.childrens.length > 0) {
result = this.addChildNodeNext(item.childrens, CurrentID)
} else {
result = item
}
return result
});
this.setState({ childIndex: index })
this.setState({ treeNode: newTree })
}

Resources