Use state not updating inside switch that is constantly updating - reactjs

I have this program that every time the cart changes it runs. It checks if specific id is in the cart (everything until now is working as intended, if i console log "state" after the for loop it will correctly return the state). Then, I have an if statement that depending on the state value it will set a value on local storage and update setValuePixel.
In both cases, state true or false, the setValuePixel will not update the valuePixel value. The console.log after the set state return false either way.
I'm not sure why this is happening so any help is appreciated. Thanks
const RemoveItemsOnDeleteKit = ({children}:any) => {
const [valuePixel, setValuePixel] = useState<string | null>('false')
const [val, setVal] = useState<string | null>('')
const handleEvents = (e: PixelMessage) => {
switch (e.data.eventName) {
case 'vtex:cartChanged': {
const { items } = e.data
let state = false
if (items != undefined || items != null) {
for (let i = 0; i <= items.length; i++) {
// console.log(items[i])
if (items[i]?.skuId == "38"){
state = true
}
}
if (state == true) {
localStorage.setItem('isKitOnCart', 'true')
setValuePixel('true')
console.log('the set value pixel value is ', valuePixel)
}
else if(state == false) {
localStorage.setItem('isKitOnCart', 'false')
setValuePixel('false')
console.log("THE SET VALUE PIXEL VALUE IS ", valuePixel)
}
else {
console.log("THE ELSE STATEMETN IS HERE!")
}
}
}
default: {
break
}
}
}
}

Related

How to properly check action.payload for existence in the store?

I'm trying create function that to place action.payload into store if it not exist there, otherwise delete it.
state.selected = []; //initial
action.payload = {...}
Slice.ts:
userSelect(state, action: PayloadAction<IUser>) {
if (state.selected.length > 0) {
for (let i = 0; i < state.selected.length + 1; i++) {
if (state.selected[i].id === action.payload.id) { //state.selected[i] -> Proxy; state.selected[i].id = undefined
state.selected.filter(e => e !== action.payload)
} else {
state.selected = state.selected || []
state.selected.push(action.payload)
}
}
} else {
state.selected = state.selected || []
state.selected.push(action.payload)
}
}
I'm trying to check array state.selected for existence action.payload by its id, but i cant get id from within state.selected, because it's Proxy type, and my logs for checking single e.g. state.selected[0] returns as Proxy {i: 0, A: {…}, P: false, I: false, D: {…}, …}
For logging, you can import { current } from '#reduxjs/toolkit' and console.log(current(state)).
Also, please note that .filter does not change your array, but returns a new one, so you would have to do state.selected = state.selected.filter(e => e !== action.payload)
Generally:
const foundIdx = state.selected.findIndex(selected => selected.id === action.payload.id)
if (foundIdx >= 0) {
state.selected.splice(foundIdx, 1)
} else {
state.selected.push(action.payload)
}
should do all you need

Sorting data (in a state) when state gets updated (React) - renders with delay

I'm completely new to React and haven't been able to figure this out.
I'm trying to sort a data array saved in a state (notes). I need this to be reusable, so the sorting happens:
after clicking on a select that determines how the data should be sorted
after modifying the data itself (e.g. changing the title or updating the content).
I tried some alternatives and settled on saving sort, i.e. how the data should be sorted, in another state, and then using useEffect like this:
const [notes, setNotes] = React.useState(notesInitial)
const [sort, setSort] = React.useState('updated')
function doSort(currentSort) {
console.log("doing sort")
switch (currentSort) {
case "updated":
setNotes(oldNotes => {
return oldNotes.sort((a,b) => {
return a.updated - b.updated
})
});
break;
case "created":
setNotes(oldNotes => {
return oldNotes.sort((a,b) => {
if (a.date > b.date) {
return 1
} else if (a.date < b.date) {
return -1
} else if (a.date == b.date) {
return 0
}
})
});
break;
case "name":
setNotes(oldNotes => {
return oldNotes.sort((a,b) => {
let titleA = a.title.toUpperCase();
let titleB = b.title.toUpperCase();
if (titleA > titleB) {
return 1
} else if (titleA < titleB) {
return -1
} else if (titleA == titleB) {
return 0
}
})});
break;
}
}
// sorting
function changeSort(event) {
setSort(event.target.value);
}
useEffect(()=>{
doSort(sort);
console.log(sort, notes)
}, [sort, notes])
I was concerned about causing an infinite loop but from what I can tell, the useEffect() isn't causing one (?). However, the correctly sorted data in notes always renders with a delay. What am I doing wrong here and what's the alternative?
You have to clone the array before you sort
return [...oldNotes].sort((a,b) => {
return a.updated - b.updated
})
Otherwise react does not detect the change.
Also you run into the problem, that the useEffect will be called when notes are changed. This is true when you sort. So it will be called again and again.

uncheck (removing) last item in checkbox and check(adding) other at the same time , keeps the unchecked value and adds other checked value

here only one person is assigned
Now, unchecked the previous person(already added), and check other other
onSubmit , it adds then both
here is my code :
const [assignCustomerId, setAssignCustomerId] = useState([]);
const handleOnChange = (id, event) => {
//checking the already present customers by id
const alreadyAssignedDeliveryMan = deliveryBoy?.assigned_customers.map(
(d) => d.milkman_buyer_customer_id
);
// here, if already customer is assigned in database then pushing new checked
// and keeping the database data also
if (assignCustomerId.length === 0) {
for (let i = 0; i < alreadyAssignedDeliveryMan.length; i++) {
assignCustomerId.push(alreadyAssignedDeliveryMan[i]);
}
} else {
console.log("null");
}
//if user checked, then push it into state array
//if user unchecked , then removing the item from array
const checked = event.target.checked;
if (checked === true) {
assignCustomerId.push(id);
setAssignCustomerId(assignCustomerId);
} else if (checked === false) {
for (var i = assignCustomerId.length - 1; i >= -1; i--) {
if (assignCustomerId[i] === id) {
assignCustomerId.splice(i, 1);
setAssignCustomerId(assignCustomerId);
}
}
}
};
Issue
You are mutating your assignCustomerId state and saving the same array reference back into state. When updating the "already assigned delivery persons" the code pushes directly into the assignCustomerId array. Also, when checked is true the code is pushing directly into the assignCustomerId array, and when checked is false the .splice does an in-place mutation
if (assignCustomerId.length === 0) {
for (let i = 0; i < alreadyAssignedDeliveryMan.length; i++) {
assignCustomerId.push(alreadyAssignedDeliveryMan[i]); // <-- state mutation!
}
} else {
console.log("null");
}
const checked = event.target.checked;
if (checked === true) {
assignCustomerId.push(id); // <-- state mutation!
setAssignCustomerId(assignCustomerId);
} else if (checked === false) {
for (var i = assignCustomerId.length - 1; i >= -1; i--) {
if (assignCustomerId[i] === id) {
assignCustomerId.splice(i, 1); // <-- state mutation!
setAssignCustomerId(assignCustomerId);
}
}
}
Solution
When adding a value to the assignCustomerId array create a shallow copy and append the new element value(s). When removing a value from the `assignCustomerId array then filter it and return a new array reference.
if (!assignCustomerId.length) {
setAssignCustomerId(state => state.concat(alreadyAssignedDeliveryMan));
} else {
console.log("null");
}
const { checked } = event.target;
if (checked) {
setAssignCustomerId(state => [...state, id]);
} else {
setAssignCustomerId(state => state.filter((el) => el !== id));
}
I think this can help you in remove part
let index = assignCustomerId.findIndex(x=>x===id)
assignCustomerId.splice(index, 1);
setAssignCustomerId([...assignCustomerId]);

How do I update 2 objects with React Hook useState

My first Object
const [message, setMessage] = useState({
Features: {Active:false},
General: {FileTimeout:0,ZipDepth:0}
});
How do I update this object?
const handleInput=e=>{
const name=e.currentTarget.name;
const value=e.currentTarget.value;
var temp = {...message}
if(name == 'active'){
if(value==='on'){
temp.Features.Active=true;
}
else{}
}
else if(name == 'timeout'){
temp.General.ZipDepth= 5;
}
else if(name == 'zipdepth'){
temp.General.FileTimeout= 7;
}
}
New Values= { Features :{Active:true}, General: {FileTimeout:7,ZipDepth:5}});
How can I update the values like this? If there is a library or something for this, I can also use it.
You are mutating your state object. Even though you create a copy temp of the message state, you are mutating the nested properties. You necessarily need to also shallow copy all nested state you are updating.
I would suggest using a functional state update to access the previous messagestate, and use aswitchstatement to cover the different cases on thename` value, returning the next state value for each one. Notice that each level of nested state is shallow copied before it's updated.
const [message, setMessage] = useState({
Features: { Active: false },
General: { FileTimeout: 0, ZipDepth: 0 }
});
const handleInput=e=>{
const { name, value } = e.currentTarget;
setMessage(message => {
switch(name) {
case 'active':
if (value === 'on') {
return {
...message, // shallow copy of state
Features: {
...message.Features, // shallow copy nested state
Active: true,
},
};
} else {
// ? return some new state
}
case 'timeout':
return {
...message, // shallow copy of state
General: {
...message.General, // shallow copy nested state
ZipDepth: 5,
},
};
case 'zipdepth':
return {
...message, // shallow copy of state
General: {
...message.General, // shallow copy nested state
FileTimeout: 7,
},
};
default:
return message; // no update, return current state
};
});
}
const [message, setMessage] = useState({
Features: {Active:false},
General: {FileTimeout:0,ZipDepth:0}
});
const handleInput=e=>{
const name=e.currentTarget.name;
const value=e.currentTarget.value;
var temp = {...message}
if(name == 'active'){
if(value==='on'){
temp.Features.Active=true;
}
else{}
}
else if(name == 'timeout'){
temp.General.ZipDepth= 5;
}
else if(name == 'zipdepth'){
temp.General.FileTimeout= 7;
}
setMessage({...temp}) // You need to call setMessage function in order to update the state.
}

Initializing useState by modifying (mapping through) values of redux state

I'm trying to initialize my state with the redux state that I have stored. However, when I try to map through one of the lists stored in the state it just resets the values that I have inside the list instead of returning their substring values (which I want). The thing is if I print mail.substring(0, mail.length -10) I see the value that I would like to assign to the variable but after assigning the value is empty.
Here comes the strange part: if I were to assign "hello" instead of mail.substring(0, mail.length-10) then it works which could make you assume that the substring would return an empty value but as I mentioned above it does not.
I guess this might be because I create a shallow copy of the redux state but I'm not sure. Could you help me resolve this, please?
const membershipData = useSelector(getCompanyMembershipDetails);
function getInitState() {
if (membershipData !== null && membershipData !== undefined) {
const newState = { ...membershipData };
newState.members.map((m) => {
const mail = m.contact.countersignEmail;
const newVal = mail.substring(0, mail.length - 10);
m.contact.countersignEmail = newVal;
return m;
});
return newState;
} else
return {
members: [getEmptyMemberStateForId(0), getEmptyMemberStateForId(1)],
membershipRates: [
getEmptyPropertyContributionForId(0),
getEmptyPropertyContributionForId(1),
],
registrationPermissions: [],
};
}
const [membersData, setMembersData] = useState(getInitState());
It was because of the shallow copy as I thought. Using cloneDeep from lodash I made a working version:
const membershipData = useSelector(getCompanyMembershipDetails);
function getInitState() {
if (membershipData !== null && membershipData !== undefined) {
const newState = _.cloneDeep(membershipData);
newState.members.map((m) => {
const mail = m.contact.countersignEmail;
const newVal = mail.substring(0, mail.length - 10);
m.contact.countersignEmail = newVal;
return m;
});
return newState;
} else
return {
members: [getEmptyMemberStateForId(0), getEmptyMemberStateForId(1)],
membershipRates: [
getEmptyPropertyContributionForId(0),
getEmptyPropertyContributionForId(1),
],
registrationPermissions: [],
};
}
const [membersData, setMembersData] = useState(getInitState());
If you use lodash the way I did above make sure to import it the following way:
import _ from "lodash";

Resources