Two similar react functions produce Inconsistent Results in rerendering - javascript

This function works properly and the component rerenders
handleRemove = (e) => {
//console.log(e);
const arrayCopy = this.state.scanlist.filter((row) => row.ref + row.lot !== e.ref + e.lot);
this.setState({ scanlist: arrayCopy });};
This function changes the state but the component does not rerender
handleAdd = (e) => {
//console.log(e);
const index = this.state.scanlist.findIndex((row) => row.ref === e.ref && row.lot === e.lot);
let scancopy = this.state.scanlist;
scancopy[index].qty = scancopy[index].qty + 1;
console.log(scancopy);
this.setState({ scanlist: scancopy });};
Does anyone see the issue? Mutation?

Using Array.prototype.filter to remove an element from an array is pretty standard, but in the second handler yes, you've a state object mutation.
handleAdd = (e) => {
const index = this.state.scanlist.findIndex((row) => row.ref === e.ref && row.lot === e.lot);
let scancopy = this.state.scanlist;
scancopy[index].qty = scancopy[index].qty + 1; // <-- state object mutation
this.setState({ scanlist: scancopy });
};
You should shallow copy the scanlist array and the element that is being updated. Array.prototype.map is a common method to shallow copy the state array, and spreading the object properties of the element that needs to be updated shallow copies it. Anytime you are updating any nested state objects you should shallow copy the parent object.
handleAdd = (e) => {
this.setState(prevState => ({
scanlist: prevState.scanlist.map(
(row) => row.ref === e.ref && row.lot === e.lot ? {
...row,
qty: row.qty + 1,
} : row)
}));
};

In the first approach filter method returns new list, whereas in the second approach it is just referencing to state array.
Try shallow copy in second approach,
let scancopy = [...this.state.scanlist]
Or
Simple deep copy approach,
let scancopy = JSON.parse(JSON.stringify(this.state.scanlist));

Related

Mutating ref value from watcher in Vue 3 with Composition API

I'm trying to mutate a ref within a watch function but it doesn't seem to work. I'm using watch to pay attention if my array length changes, whenever that changes I need to compare the old length with the new one and then mutate a ref (selectedTitle) if the newValue is less than the oldValue.
setup() {
const slots = useSlots();
const tabTitles = computed(() =>
slots.default()[0].children.map((tab) => tab.props.title)
);
const tabTitlesLength = computed(() => tabTitles.value.length);
let selectedTitle = ref(tabTitles.value[0]);
provide("selectedTitle", selectedTitle);
provide("tabTitles", tabTitles);
watch(tabTitlesLength, (currentValue, oldValue) => {
if (currentValue < oldValue) {
selectedTitle = tabTitles.value[0];
} else {
console.log("fuera del if");
}
});
return {
tabTitles,
selectedTitle,
tabTitlesLength,
};
},
With that code the selectedTitle ref never change it always has the last value that received whenever the computed property changes (tabTitles). I just don't understand why since everything else seems to work just fine.
Since selectedTitle is ref try with
selectedTitle.value = tabTitles.value[0];

React & Javascript optimization - arrays, spread, splice

I have a little problem understanding something. So I need your help to do an example for me. Please kindly write this two functions with a better solution.
the setTempRecipe is a coming from useState hook in my react functional component.
const addCustomizationOption = (co) => {
const tmpR = Object.assign({}, tempRecipe);
tmpR.customizationOptions.push(co);
setTempRecipe(tmpR);
};
and the second one is:
const removeCustomizationOption = (co) => {
const tmpR = Object.assign({}, tempRecipe);
const g = tmpR.customizationOptions.findIndex(item => item.id === co.id);
tmpR.customizationOptions.splice(g, 1);
setTempRecipe(tmpR);
};
const addCustomizationOption = (co) => {
setTempRecipe({
...tempRecipe,
customizationOptions:[...tempRecipe.customizationOptions,co]
});
};
const removeCustomizationOption = (co) => {
setTempRecipe({
...tempRecipe,
customizationOptions:tempRecipe.customizationOptions.filter(i => i.id !== co.id)
});
};
You have the right idea, you shouldn't mutate state variables, instead take a deep copy and apply new values. What I would go for is:
const addCustomizationOption = (co) => {
setTempRecipe(tr => ({...tr, customizationOptions: [...tr.customizationOptions,co]}));
};
This spread operator will add co to the tempRecipe array and tr refers to the current state.
And, for the second one, you can filter the array:
const removeCustomizationOption = (co) => {
setTempRecipe(tr => ({...tr, customizationOptions: tr.customizationOptions.filter(recipe => recipe.id !== co.id))})
};
Since .filter() function returns a new array, you can directly filter out the id and set the new filtered array.

Remove an object's key and value using a variable from function

Hey I'm trying to remove a key:value pair from state inside a Javascript Object.
It works when I hardcode the key name in the code, but when I try to use a variable from a function call, it does nothing.
Can somebody help me out?
Here's an object example:
toppingsSelected: {
"Onion":"true",
"Mushrooms":"true",
}
This works, hardcoded:
deleteTopping = toppingName => {
const { Onion, ...withoutOnion } = toppingsSelected;
console.log(withoutOnion); // Returns object without onion
};
This doesn't work:
deleteTopping = toppingName => {
const toppingName = "Onion"; // Variable gets passed in
const { toppingName, ...withoutOnion } = toppingsSelected;
console.log(withoutOnion); // Returns original object, no change made
};
So I'm basically trying to remove a key from React state but I'm pretty new to Javascript.
How can I make Javascript aware that toppingName is a key?
Another option is to add square brackets arround toppingName, and assign it to a variable. As #Bergi pointed out in the comments, this option does not mutate toppingsSelected
const toppingsSelected = {
"Onion":"true",
"Mushrooms":"true",
};
const toppingName = "Onion";
const {
[toppingName]: topping,
...withoutOnion
} = toppingsSelected;
console.log(JSON.stringify(withoutOnion));
To set the React state, you'd then do this
this.setState({ toppingsSelected: withoutOnion })
You can use delete e.g.
delete toppingsSelected[toppingName];
One way of doing this is using Array.prototype.filter()
const _obj = {
'Onion': true,
'notOnion': false
};
const newObj = Object.keys(_obj)
.filter(key => key !== 'Onion')
.reduce((acc, cur) => ({ ...acc, cur }), {})
console.log(newObj); // { notOnion: false }
This will return a new object without the 'Onion' property

create react state name with variable reference?

i want to create state like this:
componentWillReceiveProps(nextProps) {
nextProps.columns.forEach((c) => {
const name = nextProps.columns[nextProps.columns.indexOf(c)];
this.setState({ `${name}`: (this.props.activeHeaders.indexOf(c) > -1) });
console.log(`${name}`);
});
}
I am mapping on my array columns, so each item on the array, i want to set state on them as key, is there a possibe way?
Is there a possible way?
Yes, but the way you are trying is not correct, instead of calling setState inside loop, first prepare an object with all the key-value, then pass that object to setState.
Like this:
componentWillReceiveProps(nextProps) {
let obj = {};
nextProps.columns.forEach((c, i) => {
const name = nextProps.columns[nextProps.columns.indexOf(c)];
obj[name] = this.props.activeHeaders.indexOf(c) > -1;
});
this.setState(obj);
}
Didn't get the meaning of this line:
const name = nextProps.columns[nextProps.columns.indexOf(c)];
componentWillReceiveProps(nextProps) {
nextProps.columns.forEach((c) => {
const name = nextProps.columns[nextProps.columns.indexOf(c)];
this.setState({ [name]: (this.props.activeHeaders.indexOf(c) > -1) });
console.log(`${name}`);
});
}
This should do the job

React JS : How to highlight partial data changes?

I have a React JS dashboard page that poll's a json URL every second. Most of the time the data is the same but as soon as part of it changes I would like to highlight the change part.
Is there a way to determine what parts of the data changed allowing me to highlight them for a second or two?
In your child components, implement componentWillReceiveProps where you can compare newly passed props to the current ones. If you spot a difference between the two objects, set state to something like "justChanged: true" and display accordingly.
If I understood right, you're probably needing something like this one: http://codepen.io/zvona/pen/XdWoBW
let originalJSON = {foo:'bar', foz: 'baz'}
let alteredJSON = {foo: 'barbar', fam: 'bam'}
const getOriginal = function(key, original, altered) {
const obj = {}
if (altered[key] && altered[key] !== original[key]) {
// highlight here, if needed
obj[key] = altered[key]
} else {
obj[key] = original[key]
}
return obj
}
const getAltered = function(key, original, altered) {
const obj = {}
if (original[key] || altered[key] === original[key]) {
return false
} else {
// highlight here, if needed
obj[key] = altered[key]
}
return obj
}
const getFinal = function(original, altered) {
return Object.keys(original)
.map((key) => getOriginal(key, original, altered))
.concat(
Object.keys(altered)
.map((key) => getAltered(key, original, altered))
)
.filter((item) => { return item })
.reduce((prev, next) => { return Object.assign(prev, next) })
}
let final = getFinal(originalJSON, alteredJSON)
In this case, final outputs: {foo: "barbar", foz: "baz", fam: "bam"}. Now the highlighting should be done in getOriginal and getAltered functions.

Categories

Resources