Find object in state and update property - reactjs

I have a problem with a dynamic state I am setting. My first state looks like this:
const [exercises, setExercises] = useState([{
id: 123,
title: "Title here",
category: "someCategory"
}])
A user then selects an item of this state. I create a second state representing the selected object, but adding additional properties to it. For instance I am adding and initializing the properties 'amount' and 'unit'.
const [selectedExercises, setSelectedExercises] = useState([{
id: 123,
title: "Title here",
category: "someCategory",
amount: 0,
unit: ''
}])
I want the user to choose amount and unit from a form. How do I access and change those two properties in the state? Since I don't know the user's selection, I have to find the object within the state first.
I have tried things like (el being called from an input element somewhere):
setSelectedExercises([
...selectedExercises,
(selectedExercises.find(exercise => exercise.title === el.title).amount = 1),
])
How do I find the object in question and update its amount property (for example in an onChange method)?

const [selectedExercises, setSelectedExercises] = useState([{
id: 123,
title: "Title here",
category: "someCategory",
amount: 0,
unit: ''
}]);
// Your handler should look like this and
// you should call handleAmountChange(el.id, 1)
function handleAmountChange(amount, id) {
setSelectedExercises(prev => prev.map(selectedExercise => {
if (selectedExercise.id === id) {
return {
...selectedExercise,
amount
}
}
return selectedExercise;
}));
}
A more generic function to change any property would look like this.
function handleChange(id, property, value) {
setSelectedExercises(prev => prev.map(selectedExercise => {
if (selectedExercise.id === id) {
return {
...selectedExercise,
[property]: value
}
}
return selectedExercise;
}));
}

Related

Insert list data over the iteration(map)

Here I am trying to modify my data over the iteration and send some result to API call.
The API Call receives a request with a structured data format which is
{ list: [{ id: "1", name: "Hello" }, ... ] }
Somehow I managed to call the API with single data ( const params in my current code, it only accepts single data).
But now it has to be done with multiple data something like this:
{ list: [{ id: "1", name: "Hello" }, { id: "22", name: "Ed" }, { id: "36", name: "Jason" } ... ] }
Here is my current code
const [table, setTalbe] = useState(..); // assume, we have some table data here
const processNow = () => {
let id = 0;
let name = '';
// if table length is greater than 1, we go for the loop.
if (table.length >= 1) {
table.map(data => {
id = data.userId;
name = data.userName;
});
//insert table data to params, here I want to add whole table data into "list"
//the final result of this list should be something like this
//ex ) list: [{ id: '123', name: 'Josh' }, { id: '125', name: 'Sue' }, { id: '2222', name: 'Paker' } ...],
// but how??
const params: any = {
list: [
{
id: id,
name: name
},
],
};
//send PUT reqeust with params
axios
.put(
'/api/v1/tosent',
params,
)
.then(res => {
console.log('The response', res);
})
.catch(err => {
console.log('The error: ', err);
});
}
};
but I'm stuck with it, please help me to finish this code to work properly.
need your kind advice.
Array.prototype.map returns a new array with the function you pass applied to every element. You should study the MDN documentation on map to understand its use.
Your current code does nothing with the map return value:
table.map(data => {
id = data.userId;
name = data.userName;
});
You probably assumed .map would mutate the data, as in change it in place. Instead, the whole operation returns a new array.
It looks like you want to do:
const list = table.map(data => {
return {
id: data.userId,
name: data.userName
}
});
This is applying a function to every element in the array that will map each element to a new object, matching your question, with an id and name key. Then it looks like you want to pass the returned value of map (which we named list above) to your call:
const params: any = {
list: list
};

typescript how to find inside an array that is already in an array?

I want to find a value inside an array that is already inside an array.
To give an example of my array:
[
{
ConcessionId: 1,
ConcessionName: "Coyotes",
KnownAs: [
{
TeamId: 1,
Name: "Arizona Coyotes",
},
{
TeamId: 2,
Name: "Phoenix Coyotes",
}
]
},
{
ConcessionId: 2,
ConcessionName: "Devils",
KnownAs: [
{
TeamId: 3,
Name: "Colorado Rockies",
},
{
TeamId: 4,
Name: "New-Jersey Devils",
}
]
}
]
What I want is when Icall my function it returns me the team name.
For example, I the parameter value is 3, I want Colorado Rockies as a name:
public getInfo(_TeamID) {
const concession: ConcessionInfo[] = this.concessionList$.filter(function (x) {
x.KnownAs.filter( (y)=> {
y.TeamId= +_TeamID;
return y.Name;
})
})
}
I try so many different way with filter. But never get something good. Never works.
I can make a double .foreach , for each array. but I think a better method exist than making a double loop.
Thanks
Instead of using the filter method (which is in fact working similar as a for loop), you could do forEach on both arrays. For your current data structure, there is no other way around it.
getInfo = (_TeamID) => {
let teamName = '';
this.concessionList$.forEach(entry => {
entry.KnownAs.forEach(team => {
if(team.TeamId === _TeamID){
teamName = team.Name;
return; // break the loop.
}
})
});
return teamName;
}
Here is a working example
https://stackblitz.com/edit/double-for-lopp
EDIT
If you have a look at the polyfill implementation of filter from Mozilla https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter which is in equivalent to the native implementation of filter, you can see that it is looping through the whole array, the same way as a forEach loop. The difference is that the filter method will return a new array based on the boolean condition inside the callback function, while a forEach loop does not return anything.
Assuming myArray is contains the data you provided.
The following code will work if you're using Typescript 3.7 and above.
public getInfo(teamId: number): string | undefined {
const team = this.concessionList$
.map(concession => concession.KnownAs)
.reduce((a, b) => a.concat(b), [])
.find(team => team.TeamId === teamId)
return team ? team.Name : undefined
}
Usage:
this.getInfo(3) // Colorado Rockies
Ok how this work?
You have to understand what is find. For example:
const result = [{name: 'foo', age: 1}, {name: 'bar', age: 2}]
.find(people => people.name === 'foo')
console.log(result) // {name: 'foo', age: 1}

Cannot read property of undefined, but it is defined

I have this componentDidMount function:
componentDidMount() {
const { insurances } = this.props;
const insuranceId = this.props.match.params.id;
const insurance = insurances.find(insurance => insurance.id == insuranceId);
this.setState({ insurance: insurance }, () => {
console.log("insurance", this.state.insurance.tip); <--- this is what i am talking about
});
}
That console.log() returns an object. But if i change it to console.log(this.state.insurance.tip) which is one of the properties, I get the error: TypeError: Cannot read property 'tip' of undefined. I don't really understand how mounting works. Could it be because it is not mounted? If so, how do i fix it?
EDIT
console.log('insurance', insurance), before setState is corect:
id: 1, user_id: 1, tip: "Asigurare", date_exp: "1974-10-14", date_notif: "1975-10-03", …}
created_at: "2019-03-15 10:54:40"
date_exp: "1974-10-14"
date_notif: "1975-10-03"
id: 1
note: "Nam id ipsam sequi."
tip: "Asigurare"
updated_at: "2019-03-15 10:54:40"
user_id: 1
but if I do console.log(this.state.insurance) returns an object with undefined properties
EDIT 2
I think this is close to what i have https://codesandbox.io/s/n72rx0k660
The way you have defined your state is the issue.
You have it as
state = {
insurance: [
{
tip: "1"
}
]
};
Change it to :
state = {
insurance:
{
tip: "1"
}
};
For you to access it as this.state.insurance.tip it needs to be a key in the insurance object. What you have right now it this.state.insurance[0].tip
Since insurance is an array right now.
lmk if this needs more clarification.

How to remove the right element from array react?

I want to remove an element from my array when click on a specific row.
When I click on an element it does nothing or the last row gets deleted.
I tried to remove the element like this:
ondeleterow(e: any) {
const array = [...this.state.fields.columns]; // make a separate copy of the array
const index = array.indexOf(e.target.id);
if (index !== -1) {
array.splice(index, 1);
this.setState({ fields: { columns: array }});
}
}
My array/json object looks like this:
[ {index: 0, name: "person 1", age: 12},
{index: 1, name: "person 2", age: 19},
{index: 2, name: "person 3", age: 34},
]
My result should be when I click on a row with ID=1 the row with index: 1 gets deleted from my state array.
I can't give them an Id because when I submit the json structure then does not get accepted.
I feel like your Array.splice might be causing the issue here (because even though you created a new array, the objects in the array are still passed by reference).
I would recommend a completely different method of doing this operation which I've found to be far cleaner and robust.
First you have to add a unique id field to each row. (this is good practice in react anyway, instead of using index for keys).
ondeleterow(id: string) {
return (e: any) => {
const array = this.state.fields.column.filter(item => item.id != id)
this.setState({ fields: { columns: array }});
}
}
and when you're mapping over your rows, you can simply add the function to the onClick like this
<Row key={item.id} onClick={ondeleterow(item.id)} />
Never use splice in react especially with state. They directly mutate the data. Use non mutating operations like slice.
Your code should as follows
ondeleterow(e: any) {
const array = [...this.state.fields.columns]; // make a separate copy of the array
const index = array.indexOf(e.target.id);
if (index !== -1) {
array.splice(index, 1);
this.setState({ fields: {
columns: [ ...array.slice(0, index), ...array.slice(index + 1, array.length) ]
}});
}
}
You can use Array.filter. This will allow you to create a new array with only the items you want based on a certain criteria. In this case, you want an array with items that have a different ID that the one you want to remove. So it will look like this
// Actual processing
const filterByIndex = (arr, idx) => arr.filter(x => x.index !== idx);
// Your data
const json = [{
index: 0,
name: "person 1",
age: 12
},
{
index: 1,
name: "person 2",
age: 19
},
{
index: 2,
name: "person 3",
age: 34
},
];
// Printing the result
console.log(filterByIndex(json, 1));
In your React app
ondeleterow(e: any) {
const columns = this.state.fields.columns.filter(x => x.index !== e.target.id);
this.setState({ fields: { columns }});
}
Try this
onDeleteRow(e) {
const afterRemoval = this.setState.fields.columns.filter(item => item.index != e.target.id);
this.setState(prevState => ({ fields: { ...prevState.fields, columns: afterRemoval } }));
}
The other solution above sets the fields field directly, It may work but will cause problem if fields has some other attribute other than columns (those attributes will get removed)

How to push and delete object with 3 three properties using a single Toggle Button

I am going to Push some objects in an array and delete them according to there specific ID.
Now the challenge is that i wants to do the both push and delete using a single toggle button.
this.state = {
array: [{
id: 1,
name: "Abc",
checkBoxState: true
}, ]
}
handleData(label, value, id) {
let obj = JSON.stringify({
id: id,
name: label,
checkBoxState: value
});
let array = this.state.array;
array.push(obj);
console.log(array);
}
Please tell me the method how to make it possible on a single button.
For Example if i press ok button i will fetch the properties and push into an array and if i press again this button it will have to delete the object from array according to the ID.
Edit based on your comments. First check to see if the item exists in the array. If it does, delete it. If it does not add a new item. I don't have time to test but something like this may work.
this.state = {
array: [{
id: 1,
name: "Abc",
checkBoxState: true
}]
}
handleData(label, value, id) {
let array = this.state.array;
let arrayIds = Object.values
for (let item of array) {
if (item.id === id) {
let deletedObj = array.filter(item => item.id === id)
this.setState({
array: deletedObj,
})
return
}
}
let obj = JSON.stringify({
id: id,
name: label,
checkBoxState: value
});
array.push(obj);
this.setState({
array
})
}
}
console.log(this.state.array);
}

Resources