Why the value in this array (apparently) changes immediately - javascript

I couldn't find any similar question, I don't know what could be happening there, (and maybe could be something stupid) but I haven't found any clue about what could be happening.
I have this array:
const superVillains = [
{ value: '1', label: 'Thanos' },
{ value: '2', label: 'The Joker' },
{ value: '3', label: 'Ultron', disabled: true },
{ value: '4', label: 'The Riddler' },
{ value: '5', label: 'Lex Luthor' },
{ value: '6', label: 'Green Goblin' },
{ value: '7', label: 'Bain', disabled: true },
{ value: '8', label: 'The Penguin' },
{ value: '9', label: 'Doctor Octopus' },
{ value: '10', label: 'Poison Ivy' },
{ value: '11', label: 'Magneto' },
{ value: '12', label: 'Mr. Glass' },
{ value: '13', label: 'General Zod' },
{ value: '14', label: 'Red Skull', disabled: true },
{ value: '15', label: 'Baron Von Zemo' }
];
I copied this array into another called optionsState in a react state
const [optionsState, setOptionsState] = useState(superVillains);
and applied the following operations:
const index = 0;
optionsState[index]['selected'] = true;
console.log(optionsState[index]['selected']);
console.log(optionsState[index]);
console.log(optionsState);
This is the result in console:
In the first console output it seems that the selected value is true as it should, the same for the second console output, but without changing nothing in the code the third console output shows that the selected value is false.
the question is: Why does the selected value apparently changes without applying any operations on it (besides a console log statement)?
If a place another
console.log(optionsState[index]);
after the last console log it will show the same as before:
{value: "1", label: "Thanos", selected: true}
so I don't know if it is an issue with the browser or an issue with the react states or an issue with me.
Any ideas on this?

Now that you edited your question, I get it :)
You are mutating your state, and react is based in immutability (google for more), a quick fix would be :
setOptionsState(prevState => {
const oldOptions = [...prevState.optionsState];
oldOptions[index] = { ...oldOptions[index] , selected: true };
return { oldOptions };
})

As already said, you're directly mutating the state, which is a bad idea in React. To properly update the state use functional form of setState plus map:
const index = 0;
setOptionsState(options => {
return options.map((option, i) => {
if (index === i) {
return {
...option,
selected: true
}
}
return option;
})
})

optionsState[index]['selected'] = true;
You should not mutate state like this. If you want to add a new attribute to the state, you should do it using the function provided, in your case it is going to be setOptionsState.
The easiest way to achieve this should be something like this:
setOptionsState(prevState => {
const oldOptions = [...prevState.optionsState];
oldOptions[index] = { ...oldOptions[index], selected: true };
return { oldOptions };
})
Please try this approach and see if it's any different (I hope so!)

if you want to change the value of optionsState use setOptionsState because the state value is updated after the refresh if you dont use setOptionsState.

Related

React issues in setting a state object

I have the following code to set some dropdown state in my app.
let myFieldStatusDropdown = [];
let myFieldStatus = {};
const [myCriteria, setMyCriteria] = useState({
myFieldStatus: myFieldStatus?.value,
myFieldStatusDropdown: myFieldStatusDropdown
});
if (myContextData.myCriteria?.myFieldStatus) {
setMyCriteria({
...myCriteria,
myFieldStatus: myContextData.myCriteria?.myFieldStatus
});
} else {
setMyCriteria({
...myCriteria,
myFieldStatus: myCriteria.myFieldStatusDropdown[0]
});
}
myFieldStatusDropdown is an array like below and myFieldStatus is an object which is one of the options from it
[
{
code: 'Select',
value: 'Select'
}, {
code: 'AA',
value: 'A 1'
}, {
code: 'BB',
value: 'B 1'
}
]
For some reason, myFieldStatus is not getting set properly. Is there some issue in the way I am setting the same ?

How to filter an array of objects inside another array of objects

i got a array of "partners" (like below) and need to filter them by label.
partners:
[
{
name: 'Partner X',
solution: [
{
label: 'Label 1',
},
{
label: 'Label 2',
}
]
},
{
name: 'Partner Y',
solution: [
{
label: 'Label 1'
}
]
}
]
My problem is that i can't filter the "solution" array. My best try was to use a filter inside another filter, like this:
partnerFilter() {
const partnersClean = this.partners.filter(function(e) {
return e.solution.filter(function(item) {
return item.label === 'Label 2'
})
})
return partnersClean
}
The result always display both partners.
I also searched similar cases in here but there isn't with a object inside a array, they used indexOf() or includes(), both ways i couldn't make it in this case.
If anyone can help, I appreciate it.🙏
This is done like this:
partnerFilter() {
const partnersClean = this.partners.map(function(e) {
e.solution = e.solution.filter(function(item) {
return item.label === 'Label 2'
});
return e;
})
return partnersClean
}
This will remove solutions with label Label 1 in the input, keeping other fields like name untouched. All the partners will be there in the output, even if there aren't any solution objects for them.
I just solved with the comments help.
Using .some() instead of .filter() to return truthy/falsey values.
partnerFilter() {
const setPartners = this.partners
.filter(p => p.solution.some(item => item.label === 'Label 2'))
return setPartners.filter(p => p.solution)
}
var data = {partners: [{
name: 'Partner X',
solution: [{
label: 'Label 1',
},
{
label: 'Label 2',
}
]
},
{
name: 'Partner Y',
solution: [{
label: 'Label 1'
}]
}
]};
console.log(data.partners.flatMap(i=>i.solution.filter(f=>f.label==="Label 2")))

Why doesn't my javascript quiz load properly?

I am trying to make a simple quiz. Somehow I always have an error
"Vue is not defined"
However, it actually is defined right there. I don't exactly undertand what is the reason. The website link is here.
The script is right in a head of the page. I have tried to use javascript code as a separate file, but the error is the same. I am a novice in javascript, so any help would be really appreciated.
"use strict";
window.onload = function() {
var quiz = {
title: 'What superhero are you?',
questions: [{
text: "How would you describe your personality?",
responses: [{
text: 'Im serious and dark',
value: 'Batman'
},
{
text: 'Arrogant, but charming',
value: 'Superman'
},
{
text: 'Fun and easy going',
value: 'The Flash'
}
]
},
{
text: "Why did you want to become a superhero?",
responses: [{
text: 'For the thrills',
value: 'The Flash'
},
{
text: 'For justice',
value: 'Batman'
},
{
text: 'For popularity',
value: 'Superman'
}
]
},
{
text: "Who would you most hang around with?",
responses: [{
text: 'Wonder Woman',
value: 'Superman'
},
{
text: 'Green Arrow',
value: 'The Flash'
},
{
text: 'Robin',
value: 'Batman'
}
]
},
{
text: "What's your favourite colour?",
responses: [{
text: 'Black',
value: 'Batman'
},
{
text: 'Red',
value: 'The Flash'
},
{
text: 'Blue',
value: 'Superman'
}
]
},
{
text: "When do you help people?",
responses: [{
text: 'Every chance I can',
value: 'The Flash'
},
{
text: 'At night',
value: 'Batman'
},
{
text: 'When they need me to',
value: 'Superman'
}
]
},
]
};
var app = new Vue({
el: '#app',
data: {
quiz: quiz,
questionIndex: 0,
userResponses: Array()
},
methods: {
// Go to next question
next: function() {
this.questionIndex++;
console.log(this.userResponses);
},
// Go to previous question
prev: function() {
this.questionIndex--;
},
score: function() {
//find the highest occurence in responses
var modeMap = {};
var maxEl = this.userResponses[0],
maxCount = 1;
for (var i = 0; i < this.userResponses.length; i++) {
var el = this.userResponses[i];
if (modeMap[el] == null)
modeMap[el] = 1;
else
modeMap[el]++;
if (modeMap[el] > maxCount) {
maxEl = el;
maxCount = modeMap[el];
}
}
return maxEl;
}
}
});
}
If you'll check the source code of your site You will see script for Vue is not loaded. So giving error Vue is not defined. Add below script in head of your page.
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.22/dist/vue.js"></script>
You can read here, how to use Vue in your project.
You are actually not loading Vue. Make sure to load Vue before instantiating an instance of it.
You can either load the script using a CDN, as Ashish showed, or save the script to your local machine, and including it in your page.
<script src="/js/vue-2.5.22.min.js" type="text/javascript"/>
Where /js/vue-2.5.22.min.js is the relative path to the location of Vue.

Converting Array to Object for selecting objects (Refactoring / Optimizing)

While I was facing slow loading time when it iterate array to render objects, I want to change its data structure. I show table of contents for seasons. When user clicks an item, the item is marked as selected.
Here is current data structure (Array)
const seasons = [{
id: 6,
value: 'All',
}, {
id: 7,
value: 'Spring',
}, {
id: 8,
value: 'Summer',
}, {
id: 9,
value: 'Fall',
}, {
id: 10,
value: 'Winter',
}];
I'm storing selected Season Ids as an Array now
state = {selectedSeasonIds: []}
When selectedSeasonIds has id, I want to remove the id from it. Otherwise, add the id to selectedSeasonIds. (This is current approach)
if(_.includes(this.state.selectedSeasonIds, id)) {
let newSelectedSeasonIds = _.filter(this.state.selectedSeasonIds, (curObject) => {
return curObject !== id;
});
this.setState({selectedSeasonIds : newSelectedSeasonIds});
} else {
let newSelectedSeasonIds = [...this.state.selectedSeasonIds, id];
this.setState({selectedSeasonIds : newSelectedSeasonIds});
}
And here is my pseudo-code for refactoring to convert my arrays to object structure for performance. (I found searching on an object is MUCH faster than searching on the array)
Changing the array to object
const seasons = {
6 :{
id: 6,
value: 'All',
},
7: {
id: 7,
value: 'Spring',
},
8: {
id: 8,
value: 'Summer',
},
9: {
id: 9,
value: 'Fall',
},
10: {
id: 10,
value: 'Winter',
}
};
Changing Selected Seasons <- I want to store only the key(id) of the objects. But I want to use it as an object
state = {selectedSeasonIds : {}} Can I store object type state?
Here is expected logic which can be 50 times faster than array search.
if(selectedSeasonIds[id]) {
//remove
return _.omit(state.selectedSeasonIds, id); < is this right?
} else {
//add
return {...state.selectedSeasonIds, [id]:id} <- Does this look ok?
}
Well if you think this is right, you can copy and paste my code to the answer (I will edit my question too).
Otherwise, Can you provide better suggestion or find the error?
Thank you so much
I guess you have to loop through seasons in order to render them.
My first suggestion is to add selected prop in each one of them so you don't have to check in selectedSeasonsIds on every render.
In case this is not an option, you can still keep the key value approach.
onAdd(id) {
this.setState({
selectedSeasonsIds: {
...this.state.selectedSeasonsIds,
[id]: this.state.selectedSeasonsIds[id] ? false : true
}
})
}
When checking for specific season whether they are selected or not, simply:
render() {
const { seasons, selectedSeasonsIds } = this.state
return (
<div>
...
{Object.keys(seasons).map(key =>
<ItemComponent
{...propsThatYouMightNeed}
selected={selectedSeasonsIds[key]}
/>
)}
</div>
)
}
Maybe something like this? I'd recommend storing arrays and then converting as necessary for lookups.
const seasons = [{
id: 6,
value: 'All',
}, {
id: 7,
value: 'Spring',
}, {
id: 8,
value: 'Summer',
}, {
id: 9,
value: 'Fall',
}, {
id: 10,
value: 'Winter',
}];
const seasonsHash = _.keyBy(seasons, 'id');
// check for existence
const hasId = _.has(seasonsHash, id)
// remove and convert back to array
_.values(_.omit(seasonsHash, id))
// add new id
_.concat(_.values(seasonsHash), id)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

What does these ES6 syntaxes really change?

Trying to learn some functional javascript and es6 concepts.
I have an array
var _ = require('underscore');
var raw =[
{
key :"name",value:"henry"
},
{
key :"age",value:"old"
},
{
key :"food",value:"matooke"
},
{
key :"kids",value:"Acacia"
},
{
key :"garbageA",value:"kasailoA"
},
{
key :"garbageB",value:"kasasiroB"
},
]
I am trying to filter out data with garbage keys . I have two codes that return different results and I wonder why they do not return the same results.
When i write
const endShape = _(raw)
.filter(key =>!/garbage/.test(key));
console.log(endShape);
in my console it prints.
[ { key: 'name', value: 'henry' },
{ key: 'age', value: 'old' },
{ key: 'food', value: 'matooke' },
{ key: 'kids', value: 'Acacia' },
{ key: 'garbageA', value: 'kasailoA' },
{ key: 'garbageB', value: 'kasasiroB' } ]
showing that my filter dint work.
When i write
const endShape = _(raw)
.filter({key} =>!/garbage/.test(key));
console.log(endShape);
It brings a syntax error.
But when i write
const endShape = _(raw)
.filter(({key}) =>!/garbage/.test(key));
console.log(endShape);
my filter works well and it prints
[ { key: 'name', value: 'henry' },
{ key: 'age', value: 'old' },
{ key: 'food', value: 'matooke' },
{ key: 'kids', value: 'Acacia' } ]
Why is it this way ? yet i know from phat arrow syntax that its okay to write
var x = y=>y+1;
and also
var x =(y)=>y+1
Actually the first and the second key for your filter is quite different.
On the first run when you do:
const endShape = _(raw)
.filter(key =>!/garbage/.test(key));
You are passing an object from your raw array, and your check is being evaluated like:
!/garbage/.test({ key: 'name', value: 'henry' })
Which will always be evaluated to false, and then you negate it so every condition will be true, thus your filter let every entry pass.
On the second run you do:
const endShape = _(raw)
.filter(({key}) =>!/garbage/.test(key));
Where you're destructuring key from your object, and thus the test makes sense, and your filter works fine!
Hope it helps!

Categories

Resources