How is useState() updating my data here? STRANGE - reactjs

I have data from an movie-api I want to sort based on a select menu, either by year in descending order or title in alphabetical order.
Although Im only updating state in the sort function, not using the variable any where, the data thats already mapped out, and in a different array, updates accordingly. I guess its somehow related to the first change in state for each of the two differen variables?
Any idea how I should solve this correctly and why this is happening?
const sortData = (e) => {
if (e === "year"){
const yearData = data.sort(function(a, b) {
const yearA = a.Year;
const yearB = b.Year;
if (yearA < yearB) {
return -1;
}
if (yearA > yearB) {
return 1;
}
return 0;
});
setYearData(yearData);
}
if (e === "title") {
const titleData = data.sort(function(a, b) {
const titleA = a.Title.toUpperCase();
const titleB = b.Title.toUpperCase();
if (titleA < titleB) {
return -1;
}
if (titleA > titleB) {
return 1;
}
return 0;
});
setTitleData(titleData);
}
}

The sort() method sorts the elements of an array in place, so the data(state) changed without using setState (It may cause some unpredictability happened in the execution)
You can use the sort() method on a copy of the array, so it doesn't affect your state array, I guess this change may work:
use [...data].sort(...) instead of data.sort(...)

Array.sort(), in your case data.sort() updates the original array in addition to returning it. Seems like your data variable is some sort of global that gets changed during sort().

Related

Is the insertion order for an array every going to be changed after performing a map

If I have an array and I want to call map() to that array. Will the result returned from map ever going to have different order from array?
const array = [1,2,3,4,5,6,7,8,9];
const mappedArray = array.map((num) => {
return num * num;
})
console.log(mappedArray);
The mappedArray will ever be [1,4,9,16,25,36,49,64,81]
or it is possible that it changes to something else (not the same order)?
What if I make an API call inside the map like the following
const mappedArray = array.map(async (num) => {
return await api(num);
})
// remote API
function api(num) {
return dynamoDbClient.getItem(num);
}
No, map will always iterate through the array in order. Unless the array is changed elsewhere in your code, the order of the resulting array will not change. The output will always be the same if the input is the same.
Run this code 1000 times and the array will be the same every time.
const array = [1,2,3,4,5,6,7,8,9];
const mappedArray = array.map((num) => {
return num * num;
})
console.log(mappedArray);

react check array values based on specific keys returning true/false if values found

I am trying to figure out how to do this but can't seem to wrap my head around it..
I have an address object
const obj = {
"address_type":"Home",
"country":"US",
"addressLine1":"123 Any Street",
"addressLine2":"",
"city":"Any Town",
"state":"Indiana",
"state_code":"IN",
"zip":"46220-4466",
"phone":"6715551313",
"mobile_number":"",
"extn":"",
"fax":"",
"county_name":"MyCounty"
}
I want to check for any key that has a value but only specific keys
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
I want to check all keys in objProps against my address object and if any one of them contains a value return true (doesn't matter if its 1 or all 6).. If all keys don't contain a value then return false (Sometimes I will get an address object that has all null values)
I've tried various ways to accomplish this but have failed in each one.
The variation I am working on now is using reduce. While it doesn't meet my needs I thought I could check the resulting array and if length was greater than 0 than I have my answer..
Work-in-progress:
function hasAddressData(obj: any) {
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
const keysWithData = objProps.reduce((accumulator, key) => {
const propExistsOnObj = obj.hasOwnProperty(key);
let keyHasData = [];
if (obj[key].length > 0 ) {
keyHasData = obj[key]
}
if (!propExistsOnObj) {
accumulator.push(key);
} else if (keyHasData) {
const equalValueKeyIndex = accumulator.indexOf(key);
accumulator.splice(equalValueKeyIndex, 1);
}
return accumulator;
});
return keysWithData;
}
The above is messed up I know and doesn't work.. Just learning this stuff.. anyone have a suggestion or comment?
Check that .some of the objProps, when looked up on the obj, contain a value. (Either with Boolean or by comparing against '')
const obj = {
"address_type":"Home",
"country":"US",
"addressLine1":"123 Any Street",
"addressLine2":"",
"city":"Any Town",
"state":"Indiana",
"state_code":"IN",
"zip":"46220-4466",
"phone":"6715551313",
"mobile_number":"",
"extn":"",
"fax":"",
"county_name":"MyCounty"
}
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
const somePopulated = objProps.some(prop => obj[prop]);
// or prop => obj[prop] !== ''
console.log(somePopulated);
const obj = {
"address_type":"Home",
"country":"US",
"addressLine1":"",
"addressLine2":"",
"city":"",
"state":"Indiana",
"state_code":"",
"zip":"",
"phone":"6715551313",
"mobile_number":"",
"extn":"",
"fax":"",
"county_name":""
}
const objProps = ["addressLine1","addressLine2","city","state_code","zip","county_name"];
const somePopulated = objProps.some(prop => obj[prop]);
// or prop => obj[prop] !== ''
console.log(somePopulated);
function checkKeys(target, props) {
return props.some((prop) => {
return target.hasOwnProperty(prop) && target[prop];
});
}
Explanation: some iterates through the props you want to check, returning true immediately when one is found (i.e. the callback returns true). If no props are found (i.e. no callback returns true), some returns false.
hasOwnProperty ensures that you are only checking properties on target, and not looking up the prototype chain. target[prop] checks for a truthy value. You may need to modify this last check if you're going to be handling values other than strings.

React State remains immutable for states represented with lists

I have a state represented by two arrangements. I must, at certain events, update one of them. The status update function is actually executed, but the status does not change, it remains with the same values before it is invoked. The status update does occur when I refresh the page, but not when the events that invoke the SetState occur.
onUpdateNivel = (pos, level) => {
this.setState(state => {
const listaSensor = state.listaSensor;
const list = state.listaNivel.map((item, j) => {
if (j === pos) {
return level;
} else {
return item;
}
});
var sum = 0;
for (var i = 0; i < list.length; i++) {
sum = sum + list[i];
}
return {
list,
listaSensor,
sum
};
});
};
You assigned wrong variable.
Your state has
{listaSensor, listaNivel, sum}
But when you use setState, the return object is
{
list,
listaSensor,
sum
}
See the difference.
The problem was the way the state was returning. The right way is this:
return {
LevelList: LevelList,
SensorList: SensorList,
sum: sum
};

Searching JSON array using another JSON array node js

I'm trying to filter a JSON array using another JSON array criteria that I have using (filter).
Here is my code:
function filterArray(object, criteria){
return object.filter(function(obj){
for(var i=0;i<criteria.length;i++){
let criteriaEle = criteria[i];
return Object.keys(criteriaEle).forEach(function(key){
if(obj[key] == criteriaEle[key]){
return obj;
}
})
}
})
}
For example:
object = [{type:1,company:1,color:0,name:a},{type:2,company:1,color:0,name:b},{type:1,company:3,color:0,name:c},{type:4,company:1,color:0,name:d},{type:1,company:1,color:1,name:e}]
criteria = [{type:1,company:1,color:0},{type:1,company:1,color:1}]
So if I give these two arrays to the function it should return
obj = [{{type:1,company:1,color:0,name:a},{type:1,company:1,color:1,name:e}}]
I'm not sure where am I going wrong in this. Please help.
Update:
Also, I do not want to use obj.type or obj.company or object.color as parameters to search as I want to make my code maintainable and do not want to come and update it later if in future more criteria's are added.
const data = [{type:1,company:1,color:0,name:'a'},{type:2,company:1,color:0,name:'b'},{type:1,company:3,color:0,name:'c'},{type:4,company:1,color:0,name:'d'},{type:1,company:1,color:1,name:'e'}];
const criteria = [{type:1,company:1,color:0},{type:1,company:1,color:1}];
function checkCriteria(obj) {
return criteria.some(criterion => {
for (const key in criterion) {
if (criterion[key] !== obj[key]) {
return false;
}
}
return true;
});
}
const filtered = data.filter(checkCriteria);
console.log('Filtered array: ', filtered);
Here is one solution.
Here are some references
Array.some
Array.filter
Based on the comment, adding another snippet to explain the concept of closures.
const data = [{type:1,company:1,color:0,name:'a'},{type:2,company:1,color:0,name:'b'},{type:1,company:3,color:0,name:'c'},{type:4,company:1,color:0,name:'d'},{type:1,company:1,color:1,name:'e'}];
function createCriteriaValidationFunction(criteria) {
return function checkCriteria(obj) {
return criteria.some(criterion => {
for (const key in criterion) {
if (criterion[key] !== obj[key]) {
return false;
}
}
return true;
});
}
}
const criteria = [{type:1,company:1,color:0},{type:1,company:1,color:1}];
const filtered = data.filter(createCriteriaValidationFunction(criteria));
console.log('Filtered array: ', filtered);
It's the same concept as before, however, criteria was defined in the file. This time, criteria can be defined outside and can be passed in to the function. The trick is to create the checkCriteria function on the fly with criteria passed in and available in the closure. In both cases, criteria variable is available in the scope in which checkCriteria is executed.

Redux: Why making a shallow copy of one level of the state is a mistake?

While reading the documentation about updating nested state object in Redux, I stumbled upon this common mistake, which states that doing a shallow copy of the top level object is not sufficient:
function updateNestedState(state, action) {
// Problem: this only does a shallow copy!
let newState = {...state};
// ERROR: nestedState is still the same object!
newState.nestedState.nestedField = action.data;
return newState;
}
but I couldn't find out why it isn't sufficient, because technically it's working, As you can see in this fiddle:
https://codesandbox.io/s/D9l93OpDB
I'll be happy for further clarification/explanation regarding this statement, and also will be happy for an example of what is the best practice of updating such state objects.
Thanks!
Assume you have a state object like that:
let state = { a: { b: 1 } };
let cp = { ...state }
cp.a === state.a //true, that means that those are the exact same objects.
that is because the »inner« Object (state.a) is added to cp by reference. If you now do:
cp.a.b = 10;
You change the value also in state.a.b. So the mistake here is: if you want to modify state.a.b, than you have to replace that with a new Object, having a new value like so:
let cp = {
...state,
a: {
...state.a.b
b: 2
}
}
The reason for this is that you are asked to write »pure« functions, to explain consider this:
var a = { b: 1 };
//not a pure function
function foo (obj) {
obj.b++;
return obj;
}
for (var i = 0; i < 10; i++) foo(a);
//a.b = 11;
So the object is modified each call of foo(a) will produce different output and modify a global variable.
The above can lead you to really nasty bugs, which are hard to find and to prevent you from running into this prefer the below.
//a pure function
function bar (obj) {
return {
...obj,
b + 1
}
}
for (var i = 0; i < 10; i++) bar(a);
//a.b = 2
In that case a is not modified, so the output of bar(a) will always yield the same output.

Resources