reactJs setState change property on object within an array - javascript

I'm a little bit stuck. I try to override a certain object from an array with another object from another array :
onStepsLayoutChange = (layout) => {
console.log("The new layout is", layout); //Array
this.setState((prevState) => ({
layout: layout,
stepsData: prevState.stepsData.map(step => {
layout.map(l => {
if(step.identifier === parseInt(l.i)){
console.log("Match", l, step); // "l" is not empty and actually matches only if "id" is identical
return {
...step,
layout: l //I want to override "layout" with the current layout "l"
}
}
});
return step
})
}), () => console.log("LayoutChange:", this.state.layout, this.state.stepsData)); // "layout" in each step is empty
};
Whats my fail in this case?

Issue is, you are missing the default behaviour of #array.map. For each array value map will return some value by default undefined. You are running map inside map, So the final value that stepData will have is:
[[...], [...], [....] ....]
Instead of using nested map, use #array.findIndex or #array.find and return the value.
Write it like this:
stepsData: prevState.stepsData.map(step => {
let index;
index = layout.findIndex(l => step.identifier === parseInt(l.i));
if(index >= 0){
return {
...step,
layout: layout[index]
}
}
return step;
})
Check this snippet:
let a = [1,2,3,4,5];
let b = [2,3];
let result = a.map(i => {
return b.map(j => {
if(i === j)
return 0;
})
return i;
})
console.log(result);

If you stepsData must be an array of arrays you forget one return:
onStepsLayoutChange = (layout) => {
console.log("The new layout is", layout); //Array
this.setState((prevState) => ({
layout: layout,
stepsData: prevState.stepsData.map(step => {
return layout.map(l => { //here <---
if(step.identifier === parseInt(l.i)){
console.log("Match", l, step); // "l" is not empty and actually matches only if "id" is identical
return {
...step,
layout: l //I want to override "layout" with the current layout "l"
}
}
});
return step
})
}), () => console.log("LayoutChange:", this.state.layout, this.state.stepsData)); // "layout" in each step is empty
};

Related

Remove object from array in some conditions

I need to update the filters in order of some situations.
while in some data conditions I need to remove 1 item from an array.
Here is the code
useEffect(() => {
let canceled = false;
const filters = [
new CheckFilterBuilder('badges', 'Badges'),
new CategoryFilterBuilder('category', 'Categories'),
new RangeFilterBuilder('price', 'Price'),
new CheckFilterBuilder('brand', 'Brand'),
];
console.log(filters);
dispatch({ type: 'FETCH_PRODUCTS_LIST' });
shopApi.getProductsList(
state.options,
{ ...state.filters, category: categorySlug }, filters,
).then((productsList) => {
if (canceled) {
return;
}
dispatch({ type: 'FETCH_PRODUCTS_LIST_SUCCESS', productsList });
});
return () => {
canceled = true;
};
}, [dispatch, categorySlug, state.options, state.filters]);
I tried a solution with if() statement but I couldn't put it in the remove item I want is
new RangeFilterBuilder('price', 'Price'),
since it does not accept if() or anything else I couldn't do it.
You could do that by creating a filter that's empty (or has known filters already included) and add (push) additional ones based on the condition
const filters = [new CheckFilterBuilder('badges', 'Badges')];
if (condition === true) {filters.push(new CategoryFilterBuilder('category', 'Categories'))}
if (condition === true) {filters.push(new RangeFilterBuilder('price', 'Price'))}
if (condition === true) {filters.push(new CheckFilterBuilder('brand', 'Brand'))}
you could slice it out of there?
const slicer = (arr, index) => {
if (index === 0) { arr = [...arr.slice(1)]; }
else { arr = [...arr.slice(0, index),...arr.slice(index + 1)]; }
return arr; //or not. I think arrays pass by reference anyway
}

How to insert key in object based on Id

I have an array of object, I want to add key in my specifi object of array when Id is matched. I have tried this:
this.data.forEach(value => {
if (value.Id === attachmentDataId) {
AttachmentTypeId: this.attachmentRecord.AttachmentType;
}
});
But it's not working and it's not giving any error also
Try this out :
let data = [{ id: 1 }, { id: 5 }];
const attachmentDataId = 5;
const attachmentRecord = { AttachmentType: "AttachmentType" };
data.forEach(value => {
if (value.id === attachmentDataId) {
value.AttachmentTypeId = attachmentRecord.AttachmentType;
}
});
The stackblitz example: https://stackblitz.com/edit/js-nrhouh
You could use the index parameter of forEach function to access the specific object of the array.
this.data.forEach((value, i) => {
if (value.Id === attachmentDataId) {
this.data[i] = {
...this.data[i],
AttachmentTypeId: this.attachmentRecord.AttachmentType
};
}
});
Inside the if block, you could also instead do
this.data[i]['AttachmentTypeId'] = this.attachmentRecord.AttachmentType;
I just find using the spread operator cleaner.
use javascript map() method.
Map() return a new array, it takes a callback, iterates on each element in that array
const updatedData = data.map(res => {
if(res.id === attachmentDataId) {
res.AttachmentTypeId = attachmentRecord.AttachmentType;
}
return res
})

Functional JavaScript, function returns bool on condition mapping array

I need to push to the array cities if there is no such a city
getCityList() {
const { StoreInfo } = this.props;
this.emptyCityList();
return StoreInfo.map((StoreInfo, index) => {
console.log(this.cityPushCheck(StoreInfo.city));
if (this.cityPushCheck(StoreInfo.city)) {
CITY_LIST.push({
id: index, label: StoreInfo.city, value: StoreInfo.city, disabled: false
});
}
});
}
cityPushCheck(city) {
const MAP = CITY_LIST.map((CITY_LIST) => {
if (CITY_LIST.label === city) {
console.log('are equal');
return false;
}
});
return true;
}
I was trying return CITY_LIST.map((CITY_LIST) =>
without const or return CITY_LITS.map is not working
can't get it.
what I am doing wrong?
[UPDATE]
So this solution worked for me
getCityList() {
const { StoreInfo } = this.props;
this.emptyCityList();
return StoreInfo.map((StoreInfo, index) => {
if (this.cityPushCheck(StoreInfo.city)) {
CITY_LIST.push({
id: index, label: StoreInfo.city, value: StoreInfo.city, disabled: false
});
}
});
}
cityPushCheck(cityLabel) {
const cityFromList = CITY_LIST.find(city => city.label === cityLabel);
return cityFromList === undefined;
}
thanks to #quittle
It's a bit unclear what you are asking for exactly but I believe your question is how to implement cityPushCheck so that it returns false if the city is in CITY_LIST and true otherwise. Assuming CITY_LIST is an Array, there's a helpful function called find on arrays that greedily check for the presence of an entry based on a condition.
cityPushCheck(cityLabel) {
// Grabs the instance of the city from the list if it was present
const cityFromList = CITY_LIST.find(city => city.label === cityLabel);
// cityFromList will be undefined if "find" didn't have a match
return cityFromList === undefined;
}
While use map, you go city by city in your array, and for each return boolean. You might consider use filter, and then check if the filtered array have any length just once
cityPushCheck(city) {
CITY_LIST.filter(CITY => CITY.label == city).length
}
Now, if there isn't such city at array, length is 0 what is equal to false at JS

target array by passing argument in function in js/react

I have an object to collect data to send to an API:
apiData: {
colors: [],
years: [],
// ..
}
Many of the children of this apiData are arrays like colors and years, I call these 'subgroups'. A user can select a multitude of subgroups with checkboxes that trigger:
handleCheckboxColorChange(value, isChecked) {
let newApiData = '';
this.setState( (prevState) => {
if (isChecked === true) {
newApiData = {...prevState.apiData, colors: [...prevState.apiData.colors, value]}
} else {
newApiData = {...prevState.apiData, colors: [...prevState.apiData.colors.filter(item => item !== value)]
}
}
return {apiData: newApiData}
}, () => this.props.handleApiCall(this.state.apiData))
}
I use a similar function for the other 'subgroups'. For years, all that changes in the function is colors to years. So I wish to create a more general function that can take a 'subgroup' as argument to target the right array in my object. I tried to pass a third variable (a string) subGroup like so:
handleCheckboxChange(value, isChecked, subGroup) {
// ..
newApiData = {...prevState.apiData, subGroup: [...prevState.apiData.subGroup, value]}
This does not work (I guess because it is now looking for the child 'subgroup' in my object). How can I make this work?
Use bracket notation :
handleCheckboxChange(value, isChecked, subGroup) {
// ..
newApiData = {...prevState.apiData, [subGroup]: [...prevState.apiData[subGroup], value]}
To make it a bit prettier, you can use this:
handleCheckboxColorChange(value, isChecked, subGroup) {
this.setState((prevState) => {
const newState = { ...prevState }
newState[subGroup] = isChecked ? [ ...newState[subGroup], value ] : newState[subGroup].filter(item => item !== value)
return newState
}, () => this.props.handleApiCall(this.state.apiData))
}

Chaining Immutable.js methods with deeply nested Maps/Lists

I have an Immutable.js collection that looks like this:
{ groups: Immutable.Map({
{
id: 1,
members: Immutable.List
},{
id: 2,
members: Immutable.List
}
})
}
I'm getting an updated members array and the group id from my server. How can I find the correct group by id and then update the members prop and then return a new groups collection?
I have something like this so far that doesn't work.
return state.update('groups', groups => {
for (let group of groups) {
if (group.id === action.id) {
group.members = Immutable.fromJS(action.members);
// not sure what I would do with updated here
//const updated = group.members.set('members', action.members);
}
}
return groups;
};
Also, tried this and it doesn't seem to complete, because the console.log never fires.
let update = state.get('groups')
.find(group => group.id === action.id)
.set('members', action.members)
console.log(update) // nothing, no error either
The following works, but there has to be a better, more elegant way than using fromJS and then toJS.
return state.update('groups', groupsImmutable => {
const groups = groupsImmutable.toJS();
for (let group of groups) {
if (group.id === activeId) {
group.members = action.members;
break;
}
}
return Immutable.fromJS(groups);
});
thanks!
This would work:
return state.update('groups', groups => {
return groups.map(group => {
if (group.get('id') === action.id) {
return group.set('members', Immutable.fromJS(action.members))
} else {
return group
}
})
})
You could also use findIndex to get the index of the matching group id and then use setIn to update the members array:
const updateIndex = state.get('groups').findIndex(group => group.get('id') === action.id);
if (updateIndex === -1) { // action.id not found
return state;
} else {
return state.setIn(['groups',updateIndex,'members'], Immutable.fromJS(action.members))
}
Your examples failed because you were trying to directly modify a child element, that's not how Immutable works. You need to update the entire state object with the new element.

Categories