Using forEach on a nested object key react javascript - javascript

I have a nested object:
const refsObject = {
refOne: {
current: {
validate: function()
}
},
refTwo: {
current: {
validate: function()
}
}
};
I need to run input field validation using a single method that loops through each nested object of objects and invokes the validate method.
What I have tried up until now:
const splitObject = o => Object.keys(o).map(e => ({ [e]: o[e] }));
splitObject.forEach(ref => ref.current && ref.current.validate());
SplitObject returns me an array of objects with all the objects inside refsObject. Once I do forEach on splitObject it becomes undefined since ref.current is inside the nested object key "refOne" which is dynamic so I cannot hardcode. Any suggestion helps!

Is this what you're looking for?
const refsObject = {
refOne: {
current: {
validate: function() {
console.log('validate1');
}
}
},
refTwo: {
current: {
validate: function() {
console.log('validate2');
}
}
}
};
Object.values(refsObject).map(refs => refs.current.validate());

Run the validations on the original refsObject by taking the values, and iterating them with Array.forEach(). Use conditional chaining ?. to call the function in case current might be undefined:
const refsObject = {refOne:{current:{validate(){console.log('refOne')}}},refTwo: {current: {validate() {console.log('refTwo')}}}};
const runValidations = o => Object.values(o)
.forEach(ref => ref.current?.validate());
runValidations(refsObject);

Related

Smarter way of using filter and map instead of filter and loop

I want to create a smarter way of coding of the following example. Important is that each loop (for activeFilters) needs to be fully done, before we want to return the filtersTest.
const createFilters = async () => {
const filtersTest = [] as any
// Only create active filters by checking count.
const activeFilters = getComponentFilter.value.filter(function(item) {
if (item.items) {
return item.items.some((obj) => obj.count)
}
});
// Loop through the active filters and push arrays into the object.
for(let i = 0 ; i < activeFilters.length; i++) {
const options = await createFilterOptions(activeFilters[i].id, activeFilters[i].items);
const array = {
defaultValue: null,
id: activeFilters[i].id,
value: 'nee',
label: activeFilters[i].label,
options: options,
}
filtersTest.push(array)
}
return filtersTest;
}
First of all, it should be clear that createFilters is not going to return the array, but a promise that will eventually resolve to that array.
With that in mind, you can reduce your code a bit, using Promise.all, the ?. operator, destructuring parameters, and shorthand property names in object literals:
const createFilters = () => Promise.all(
getComponentFilter.value.filter(({items}) =>
items?.some((obj) => obj.count)
).map(({id, label, items}) =>
createFilterOptions(id, items).then(options => ({
defaultValue: null,
id,
value: 'nee',
label,
options
}))
)
);

Trying to combine a .forEach and a .map function but I get return undefined

I have the following .map function that creates a new array of elements and changes their selected property to false:
const newAlteredData = alteredData.map((element) => {
return { ...element, selected: false };
});
However, now I want to only change the elements that also exist in another array called changeRows
I tried the following:
const newAlteredData = alteredData.map((element) => {
changeRows.forEach((changeRow) => {
if (changeRow.deviceId === element.deviceId) {
return { ...element, selected: false };
}
return element;
});
});
However, this just return an array of undefined. Am I not using .map and/or .forEach correctly here?
Am I not using .map and/or .forEach correctly here?
forEach() is NOT the right method to use here. It is meant for iterating over an array - not finding elements inside it.
forEach() method doesn't not returns anything. Any value returned from its callback function is ignored.
Javascript provides multiple methods to find an element inside an array.
You can use the find method to find an element in the changeRows array.
const newAlteredData = alteredData.map((element) => {
const exists = changeRows.find(el => el.deviceId === element.deviceId);
if (exists) {
return { ...element, selected: false };
}
});
Other methods that can be used:
findIndex
includes
some
As #sirius mentioned you forgot to return something from the forEach.
I would fix it this way:
const newAlteredData = alteredData.map((element) => {
const arr = []
changeRows.forEach((changeRow) => {
if (changeRow.deviceId === element.deviceId) {
arr.push({ ...element, selected: false });
}
arr.push(element);
});
return arr
});
You can not return any thing inside a forEach.
Change this:
const newAlteredData = alteredData.map((element) => {
changeRows.forEach((changeRow) => {
if (changeRow.deviceId === element.deviceId) {
return { ...element, selected: false };
}
return element;
});
});
TO
const newAlteredData = alteredData.map((element) => {
changeRows.find(changeRow => changeRow.deviceId === element.deviceId) {
return { ...element, selected: false };
}
return element;
});
forEach don't return anything in javascript.
You can use filter to achieve what you want and map through only the existing elements.
const newAlteredData = alteredData.filter(
element => changeRows.find(
changeRow => changeRow.deviceId === element.deviceId
)
).map((element) => {
return { ...element, selected: false };
});

Change Nested State Value With A Single Function? (javascript/React)

I have some react user privilege state data I need to manage. I would like the ability to change the object privileges based on their property through a dynamic function. I'm not sure how to target the specific nested privilege property to change the value. Is this possible?
Question: How can I change the value of a nested privilege property to the functions type and value parameter?
Heres an Example:
const [userPrivilages, setUserPrivilages] = useState([{
_id: "123"
privilages: {
edit: true, //before!
share: true,
del: false
}
},
{
...more users
}
])
//my attempt
const changePrivilage = (type, value) => {
const newPrivilages = userPrivilages.map(user => {
return {
...user,
privilages: {
...privilages,
//change the privilage of "type" from the functions parameter to the value parameter
}
}) setUserPrivilages(newPrivilages)
}
changePrivilage("edit", false)
Desired output:
const [userPrivilages, setUserPrivilages] = useState([{
_id: "123"
privilages: {
edit: false, //After!
share: true,
del: false
}
},
{
...more users
}
])
Thanks!
You can use [] to refer to variable as a key like below:
const changePrivilage = (type, value) => {
const newPrivilages = userPrivilages.map(user => {
return {
...user,
privilages: {
...user.privilages,
[type]: value // here it is !!!
}
}) setUserPrivilages(newPrivilages)
}
Try this :
(see comments for understanding code)
const changePrivilage = (type,value) => {
const newUserPrivilages = userPrivilages.map(user => {
let newPrivilages = user.privilages; // get old privilages of user
newPrivilages[type] = value; // update type with new value
return {
...user,
privilages: newPrivilages, // set privilages as newPrivilages
};
});
setUserPrivilages(newUserPrivilages);
};
Note : this will change properties for all users. If you want to update only for specific user, pass _id as well to changePrivilage and execute newPrivilages[type] = value; // update type with new value inside if condition comparing user _id.

Edit function not saving changes to state data in React

I am trying to provide functionality in my webpage for editing state data.
Here is the state structure
state = {
eventList:[
{
name: "Coachella"
list: [
{
id: 1,
name: "Eminem"
type: "rap"
}
{
id: 2,
name: "Kendrick Lamar"
type: "rap"
}
]
}
]
}
I want to be able to edit the list arrays specifically the id, name, and type properties but my function doesn't seem to edit them? I currently pass data I want to override id name and type with in variable eventData and an id value specifying which row is selected in the table which outputs the state data.
Here is the function code:
editPickEvent = (eventData, id) => {
const eventListNew = this.state.eventList;
eventListNew.map((event) => {
event.list.map((single) => {
if (single.id == id) {
single = eventData;
}
});
});
this.setState({
eventList: eventListNew,
});
};
When I run the code the function doesn't alter the single map variable and I can't seem to pinpoint the reason why. Any help would be great
edit:
Implementing Captain Mhmdrz_A's solution
editPickEvent = (eventData, id) => {
const eventListNew = this.state.eventList.map((event) => {
event.list.map((single) => {
if (single.id == id) {
single = eventData;
}
});
});
this.setState({
eventList: eventListNew,
});
};
I get a new error saying Cannot read property list of undefined in another file that uses the map function to render the state data to the table?
This is the part of the other file causing the error:
render() {
const EventsList = this.props.eventList.map((event) => {
return event.list.map((single) => {
return (
map() return a new array every time, but you are not assigning it to anything;
editPickEvent = (eventData, id) => {
const eventListNew = this.state.eventList.map((event) => {
event.list.forEach((single) => {
if (single.id == id) {
single = eventData;
}
});
return event
});
this.setState({
eventList: eventListNew,
});
};
const editPickEvent = (eventData, id) => {
const updatedEventList = this.state.eventList.map(event => {
const updatedList = event.list.map(single => {
if (single.id === id) {
return eventData;
}
return single;
});
return {...event, list: updatedList};
});
this.setState({
eventList: updatedEventList,
});
}
Example Link: https://codesandbox.io/s/crazy-lake-2q6ez
Note: You may need to add more checks in between for handling cases when values could be null or undefined.
Also, it would be good if you can add something similar to the original data source or an example link.
Turns out primitive values are pass by value in javascript, which I didn't know and why the assignment wasn't working in some of the previous suggested answers. Here is the code that got it working for me:
editEvent = (EventData, id) => {
const eventListNew = this.state.eventList.map((event) => {
const newList = event.list.map((single) => {
return single.id == id ? EventData : single;
});
return { ...event, list: newList };
});
this.setState({
eventList: eventListNew,
});
};

Which approach in React is better?

Below both code does exactly same but in different way. There is an onChange event listener on an input component. In first approach I am shallow cloning the items from state then doing changes over it and once changes are done I am updating the items with clonedItems with changed property.
In second approach I didn't cloned and simply did changes on state items and then updated the state accordingly. Since directly (without setState) changing property of state doesn't call updating lifecycles in react, I feel second way is better as I am saving some overhead on cloning.
handleRateChange = (evnt: React.ChangeEvent<HTMLInputElement>) => {
const {
dataset: { type },
value,
} = evnt.target;
const { items } = this.state;
const clonedItems = Array.from(items);
clonedItems.map((ele: NetworkItem) => {
if (ele.nicType === type) {
ele.rate = Number(value);
}
});
this.setState({ items: clonedItems });
};
OR
handleRateChange = (evnt: React.ChangeEvent<HTMLInputElement>) => {
const {
dataset: { type },
value,
} = evnt.target;
const { items } = this.state;
items.map((ele: NetworkItem) => {
if (ele.nicType === type) {
ele.rate = Number(value);
}
});
this.setState({ items });
};
You can use this
this.setState(state => {
const list = state.list.map(item => item + 1);
return {
list,
};
});
if you need more info about using arrays on states, please read this: How to manage React State with Arrays
Modifying the input is generally a bad practice, however cloning in the first example is a bit of an overkill. You don't really need to clone the array to achieve immutability, how about something like that:
handleRateChange = (evnt: React.ChangeEvent<HTMLInputElement>) => {
const {
dataset: { type },
value,
} = evnt.target;
const { items } = this.state;
const processedItems = items.map((ele: NetworkItem) => {
if (ele.nicType === type) {
return {
...ele,
rate: Number(value)
};
} else {
return ele;
}
});
this.setState({ items: processedItems });
};
It can be refactored of course, I left it like this to better illustrate the idea. Which is, instead of cloning the items before mapping, or modifying its content, you can return a new object from the map's callback and assign the result to a new variable.

Categories

Resources