REACT: Updating nested state - reactjs

I have two sets of data. Both are saved in a states.I want to update the rowsData inside the data1 state based on the values in data2 state. The "row" value in the data2 object refers to the "id" of rowsData in the data1 state and columns in the data2 refers to any data beside id in the rowsData object in data1. I want to pick "row" and "column" from data2 and cahnge the respective data inside rowsData in data1.
let tableData = {
columnsDef: [
{ title: "id",field: "id",className: "header-style" },
{ title: "First_name", field: "First_name", className: "header-style" },
{ title: "Last_name", field: "Last_name", className: "header-style" },
{ title: "Email", field: "Email", className: "header-style" },
],
rowsData:[
{ id: "1", First_name: "Donald", Last_name: "OConnell", Email: "DOCONNEL" },
{ id: "2", First_name: "Douglas", Last_name: "Grant", Email: "DGRANT" },
{ id: "3", First_name: "Jennifer", Last_name: "Whalen", Email: "JWHALEN" },
{ id: "4", First_name: "Michael", Last_name: "Hartstein", Email: "MHARTSTE" },
{ id: "5", First_name: "Pat", Last_name: "Fay", Email: "PFAY" },
{ id: "6", First_name: "Susan", Last_name: "Mavris", Email: "SMAVRIS" },
{ id: "7", First_name: "Hermann", Last_name: "Baer", Email: "HBAER" }
],
file: [
{ file: { path: "dummy_data_3 .csv"}}
],
}
let updatedTableData = [
{ "row": 2, "column": "Email", "oldValue": "DGRANT", "newValue": "DGRANT UPDATED" },
{ "row": 6, "column": "First_name", "oldValue": "Susan", "newValue": "SUSAN UPDATED" },
{ "row": 4, "column": "Last_name", "oldValue": "Hartstein", "newValue": "Hartstein UPDATED" }
]
const [data1, setData1] = useState(tableData)
const [data2, setData2] = useState(updatedTableData)
Here is the codesandbox link for the issue.
https://codesandbox.io/s/reverent-firefly-r87huj?file=/src/App.js

You can follow these steps for updating data1 from handleAppyChanges function:
Create a copy of rowsData:
const td = [...data1.rowsData];
Iterate over data2 and find related item, then update its related column:
data2.forEach((item) => {
let row = data1.rowsData.find((r) => r.id === item.row);
if (row) {
let index = data1.rowsData.findIndex((v) => v === row);
row[item.column] = item.newValue;
td[index] = row;
}
});
Update table data via state:
const newData = {
file: data1.file,
rowsData: td,
columnDef: data1.columnsDef
};
setData1(newData);
Here's the full function:
const handleAppyChanges = () => {
const td = [...data1.rowsData];
data2.forEach((item) => {
let row = data1.rowsData.find((r) => r.id === item.row);
if (row) {
let index = data1.rowsData.findIndex((v) => v === row);
row[item.column] = item.newValue;
td[index] = row;
}
});
const newData = {
file: data1.file,
rowsData: td,
columnDef: data1.columnsDef
};
setData1(newData);
console.log("Updated Data 1: ", td);
};
You can access the full code via codesandbox:

Apply into onChange and check it.
const handleAppyChanges = () => {
data2.map((item) => {
data1.rowsData.map((data) => {
//match both id and row
if (data.id == item.row) {
//check column is there or not
let column = data.hasOwnProperty(item.column);
if (column) {
//if yes then change the value
data[item.column] = item.newValue;
}
}
});
});
setData1({ ...data1 });
};

Related

How do I filter through a nested array in react?

I'm trying to filter through an array and then a nested array within that array, in the same function.
It works fine while filtering through keysFirst and keysSecond.
The person array has two nested arrays: skills and roles. The skills array has an attribute of name and an attribute of stars. Here I want to filter on the name attribute.
How do I make a function where I'm able to filter through either keysFirst and/or keysSecond and also through one or both of the nested arrays?
Hope that makes sense.
Homepage.jsx
const [queryText, setQuery] = useState("");
const [querySecond, setQuerySecond] = useState("");
const [queryThird, setQueryThird] = useState("");
const keysFirst = ["firstname", "lastname"];
const keysSecond = ["location", "department"];
const keysThird = ["skills", "roles"];
const [data, setData] = useState([
{
firstname: "Brad",
lastname: "Pitt",
location: "America",
department: "Hollywood",
skills: [
{ name: "Actor", stars: 5 },
{ name: "Fighting", stars: 4 },
],
roles: [
{ name: "Snatch", salary: 1000 },
{ name: "Fightclub", salary: 2000 },
],
},
{
firstname: "Jason",
lastname: "Statham",
location: "England",
department: "Hollywood",
skills: [
{ name: "Driving", stars: 2 },
{ name: "Taxi", stars: 4 },
],
roles: [
{ name: "Crank", salary: 3000 },
{ name: "Transporter 2", salary: 4000 },
],
},
]);
const searchFunction = (data) => {
const personFiltered = data.filter(
(item) =>
keysFirst.some((keysFirst) =>
item[keysFirst].toLowerCase().includes(queryText.toLowerCase())
) &&
keysSecond.some((keysSecond) =>
item[keysSecond].toLowerCase().includes(querySecond.toLowerCase())
)
);
return personFiltered;
};

Remove object in array

I have a array object and a array.
const arr1=[
{ name: "viet" },
{ name: "hai" },
{ name: "han" }
]
const arr2= ["viet", "hai"];
How can i compare and set arr like:
arr = [{name: "han"}]
Try this code :D
const arr1=[
{ name: "viet" },
{ name: "hai" },
{ name: "han" }
]
const arr2= ["viet", "hai"];
const result = arr1.filter(item => !arr2.includes(item.name))
console.log(result) // [{name: "han"}]
const arr1=[
{ name: "viet" },
{ name: "hai" },
{ name: "han" }
]
const arr2= ["viet", "hai"];
let res = arr1.filter(function (n) {
return !this.has(n.name);
}, new Set(arr2));
console.log(res);

Adding object in Array in nested object with setState

I'd like to know how to add an object in datasets,
I'm trying to add an object in array with using setState,
but It doesn't work at all .
my code is like this :
const [DataContext, setDataContext] = useState([
{
labels: defaultLabels,
datasets: [
{
label: "dataSetting",
data: defaultDatas,
backgroundColor: defaultBackgroundColor,
},
],
},
{
labels: defaultLabels,
datasets: [
{
label: "dataSetting",
data: defaultDatas,
backgroundColor: defaultBackgroundColor,
},
],
},
{
labels: defaultLabels,
datasets: [
{
label: "dataSetting",
data: defaultDatas,
backgroundColor: defaultBackgroundColor,
},
],
},
const addAxis = index => {
let addAxis = [...DataContext];
addAxis[index].datasets.push({ label: "", data: "", background: "" });
setDataContext(addAxis);
};
You need a deep copy to update the state:
const addAxis = index => {
setDataContext(dataContext => dataContext.map((data, idx) => {
return idx === index ? {
...data,
datasets: [...data.datasets, { label: "", data: "", background: "" }]
} : data
})
};
You need to deep copy DataContext.
const _copy = (value) => {
let object;
if (Array.isArray(value)) {
object = [];
value.forEach((item, index) => {
if (typeof value[index] === 'object') {
object[index] = _copy(value[index]);
} else {
object[index] = value[index];
}
});
} else {
object = {};
for (let key in value) {
if (typeof value[key] === 'object') {
object[key] = _copy(value[key]);
} else {
object[key] = value[key];
}
}
}
return object;
};
const addAxis = index => {
let addAxis = _copy(DataContext);
addAxis[index].datasets.push({ label: "", data: "", background: "" });
setDataContext(addAxis);
};

Update a selected property from react state of objects with arrays

Assume that this state has initial data like this
const [options, setOptions] = useState({
ProcessType: [
{ value: 1, label: 'Type1' }, { value: 2, label: 'Type2' }
],
ResponsibleUser: [
{ value: 1, label: 'User1' }, { value: 2, label: 'User2' }
]
});
The following function will be called again and again when a post/put called
Help me to complete the commented area as described there.
const fetchProcesses = async () => {
await axios.get(`${process.env.REACT_APP_SERVER_BASE_URL}/processes/`)
.then((result) => {
/*
I want here to clear the existing data in options.ProcessType and
map result.data as { value: result.data.id , label: result.data.name },....
and push/concat it into to options.ProcessType but i want to keep the data
inside options.ResponsibleUser unchanged.
result.data is an array of objects like this,
[
{ id: 1 , name: 'Type1', desc : 'desc1', creator: 3, status: 'active' },
{ id: 2 , name: 'Type2', desc : 'desc2', creator: 6, status: 'closed' },
.....
.....
]
*/
})
}
Here is a solution
const fetchProcesses = async () => {
await axios.get(`${process.env.REACT_APP_SERVER_BASE_URL}/processes/`)
.then((result) => {
// solution
setOptions({ResponsibleUser: [...options.ResponsibleUser], ProcessType: result.data.map(row => ({value: row.id, label: row.name}))})
})
}

Pushing items from first array of objects to another array of objects

defaultParameterTypes = [] // this is my empty array
const propsArray = this.props.device.deviceProperties.defaultParameterTypes;
const defaultParameterTypes = this.state.deviceSettings.defaultParameterTypes;
when i checked on checkbox i want to push into my defaultParameterTypes array the object but the case is
when the type is repeated dont push it twice.
"propsArray": "[{'name': '1','type': '11'}, {'name': '2','type': '22'}, {'name': '3','type': '11'}, {'name': '4','type': '11'}, {'name': '5','type': '22'}, {'name': '6','type': '22'}, {'name': '7','type': '22'}]",
i want to make like loop and check if it is not found in defaultEmptyArray and push it if not found
but i dont need the type to be repeated in mydefaultarray
Simple way would be to create a map of the types seen in the array and reduce your input array into it, then get the array of values from the map you created.
const data = [
{ name: "1", type: "11" },
{ name: "2", type: "22" },
{ name: "3", type: "11" },
{ name: "4", type: "11" },
{ name: "5", type: "22" },
{ name: "6", type: "22" },
{ name: "7", type: "22" }
];
// reduce array into map of type => { name, type }
// then get the object values array
const reducedData = Object.values(
data.reduce((acc, { name, type}) => {
if (!acc[type]) acc[type] = { name, type }; // if not seen type, store
return acc;
}, {})
);
console.log(reducedData)
Expand on this concept to create a function that takes two arrays and processes the second into the first.
const data = [
{ name: "1", type: "11" },
{ name: "2", type: "22" },
{ name: "3", type: "44" },
{ name: "4", type: "11" },
{ name: "5", type: "22" },
{ name: "6", type: "33" },
{ name: "7", type: "22" }
];
const data2 = [
{ name: "1", type: "33" },
{ name: "2", type: "22" },
{ name: "3", type: "66" },
{ name: "4", type: "11" },
{ name: "5", type: "22" },
{ name: "6", type: "44" },
{ name: "7", type: "22" }
];
const data3 = [
{ name: "1", type: "66" },
{ name: "2", type: "22" },
{ name: "3", type: "33" },
{ name: "4", type: "11" },
{ name: "5", type: "55" },
{ name: "6", type: "11" },
{ name: "7", type: "44" }
];
const reduceData = (currentArray = [], newArray = []) => {
const mapFn = (acc, { name, type }) => {
if (!acc[type]) acc[type] = { name, type }; // if not seen type, store
return acc;
};
const createMap = array => array.reduce(mapFn, {});
return Object.values(newArray.reduce(mapFn, createMap(currentArray)));
};
const reducedData = reduceData(data, data2);
const reducedData1 = reduceData(reducedData, data3);
console.log(reducedData);
console.log(reducedData1);
let hasValue = (arr, obj) => arr && arr.filter(item => item.type == obj.type).length
let result = propsArray.reduce((acc, curr) => {
if (hasValue(acc, curr)) {
return acc;
} else {
return [...acc, curr]
}
}, []);
will give u an array with all the elements where the type property is unique..
hope this is what ur expected result..
let result = propsArray.reduce((acc, curr) => {
if (hasValue(acc, curr)) {
return [...acc, { name: curr.name }];
} else {
return [...acc, curr]
}
}, []);
or if type reccurs, copy only the name object..
this will return an array of same length but along the elements, type property will be removed if it recurrs...

Resources