ReactJS: How to update the data in array state - javascript

const [rowData, setRowData] = useState([]);
const old = {id: 'stud1', name: 'jake', room: '2'};
const newData = {name: 'jake', room: '3A'};
useEffect(() => {
let ignore = false;
(async function getUsers() {
let response = await getAll({ length: 999 });
if (!ignore) setRowData(response['data']['data']);
})()
return () => {
ignore = true;
}
}, []);
(async function updateItem() {
await update(oldData.id, newData).then(response => {
//value of response['data'] = {id: 'stud1', name: 'jake', room: '3A'}
setRowData(arr => [...arr, response['data']]);
}).catch(err => {
console.log(err);
})
})()
How to update the array list without appending a new data. cause I tried this setRowData(arr => [...arr, response['data']]); then it keeps appending a data.
Instead it will update the value in the array it will append on it.

Since you want to update one item within the state array, you can use map and update the item based on the id like below
(async function updateItem() {
await update(oldData.id, newData).then(response => {
setRowData(arr => arr.map(item => {
if (item.id === response.data.id) {
return response.data;
}
return item;
});
}).catch(err => {
console.log(err);
})
})()

I do not think you need a spread operator.. you can just use array.map().. maybe something like this will help you..
const old = [{id: 'stud1', name: 'jake', room: '2'},{id: 'stud2', name: 'jack', room: '2'}];
const newData = {id: 'stud1', name: 'jakey', room: '3A'};
const x = old.map((stud) => {
if(stud.id === newData.id){
stud = newData;
}
return stud;
});
console.log(x);
then you can use the x for setRowData(x)

Related

Only last item is added to array inside map function

I'm mapping an array inside a map function and I want to add the id of every element inside the array to a state. I'm facing an issue where just the last item is added to the array even though console log shows that the function iterates to correct number of times.
This is the code I have written
const { evc } = this.props;
evc.chargingStationGroups && evc.chargingStationGroups.map((item, key) => {
item.stations.map((stationItem, key) => {
console.log("stationID ",stationItem.stationID);
var stationId = {};
stationId = {
stationId: stationItem.stationID
}
var allIdArray = this.state.stationIdArray.concat(stationId);
this.setState({ stationIdArray: allIdArray })
})
})
Here evc.chargingStationGroups is something like this
[
{
groupId: "16",
groupName: "Sia",
stations: [{stationId: "8", name: "Test"},{stationId: "9", name: "Test2"},{stationId: "10", name: "Test3"}]
},
{
groupId: "17",
groupName: "Maroon5",
stations: [{stationId: "10", name: "Test"},{stationId: "11", name: "Test2"},{stationId: "10", name: "Test3"}]
}
],
How can i add all stationItem.stationID to my array, not just the last one.
Only call setState once inside all your rendering (because setState is asynchronous)
Assuming you don't have dupes of station between chargingStationGroups, just concat everybody
const { evc } = this.props;
if (evc.chargingStationGroups) {
const ids = evc.chargingStationGroups.flatMap((item, key) => {
return item.stations.map((stationItem, key) => {
return {
stationId: stationItem.stationID
}
})
})
const stationIdArray = this.state.stationIdArray.concat(ids)
this.setState({ stationIdArray })
})
Else just avoid the dupes...
const { evc } = this.props;
if (evc.chargingStationGroups) {
const ids = evc.chargingStationGroups.flatMap((item, key) => {
return item.stations.map((stationItem, key) => {
return {
stationId: stationItem.stationID
}
})
})
const arr = this.state.stationIdArray.concat(ids)
const s = new Set(arr.map(x => x.stationID))
const stationIdArray = [...s].map(stationId => ({ stationId }))
this.setState({ stationIdArray })
})
Not tested because no minimal reproducible example given, but you get the idea...
Original answer: What happens when using this.setState multiple times in React component?
In brief, this.setState is batched and called only once at the end of the loop, leaving this.state.stationIdArray empty all the time. Hence only the result at the final iteration of this statement is kept:
var allIdArray = this.state.stationIdArray.concat(stationId);
Avoid calling setState multiple time in this case:
const { evc } = this.props;
if (evc.chargingStationGroups) {
let allIdArray = [];
evc.chargingStationGroups.forEach(item => {
allIdArray = [
...allIdArray,
...item.stations.map(stationItem => ({
stationId: stationItem.stationId
}))
];
});
this.setState({ stationIdArray: allIdArray });
}
A simple example: https://codesandbox.io/s/bold-swartz-leto5
You should just use forEach if you want to do operations during iteration.
const { evc } = this.props;
evc.chargingStationGroups && evc.chargingStationGroups.forEach((item, key) => {
item.stations.forEach((stationItem, key) => {
console.log("stationID ",stationItem.stationID);
var stationId = {};
stationId = {
stationId: stationItem.stationID
}
var allIdArray = this.state.stationIdArray.concat(stationId);
this.setState({ stationIdArray: allIdArray })
})
})

JavaScript Filter return on react app delete all object with the same id

i try to return all object the !== from the object that i deleted now and the filter delete all object with the same name that i clicked.
In my application it is possible to have several identical objects and I want that at the moment of deletion it will be deleted only 1.
here the code
handleClick = packageID => {
let array = [];
packageMethods
.deletePackageFromUser(packageID, this.props.userID)
.then(res => {
packageMethods.findPackage(packageID).then(temppackage => {
array = this.state.package.filter(item => {
return item._id !== temppackage.data[0]._id;
});
this.setState({
package:array
})
});
});
};
You want to find the index of the first instance of an object with that id, then remove the item at that index.
handleClick = packageID => {
let array = [...this.state.package];
packageMethods
.deletePackageFromUser(packageID, this.props.userID)
.then(res => {
packageMethods.findPackage(packageID).then(temppackage => {
const removal_index = array.findIndex(item => {
return item._id === temppackage.data[0]._id;
});
array.splice(removal_index, 1);
this.setState({
package: array
})
});
});
};
Here's an example
const arr = [{id: 1, val: 'a'}, {id: 1, val: 'a'}, {id: 2, val: 'b'}];
console.log(arr);
const removal_index = arr.findIndex(el => el.id == 1); // find index of first object with id of 1
arr.splice(removal_index,1); // remove 1 element starting at `removal_index`
console.log(arr);

How to run fetch() in a loop?

I am new to nodejs and promise based request. I want to fetch the data from a remote server in a loop, and then create a JSON object from all fetched data.
const fetch = require('node-fetch');
const users = [];
const ids = await fetch('https://remote-server.com/ids.json');
console.log(ids);
// [1,2,3]
ids.forEach(id => {
var user = await fetch(`https://remote-server.com/user/${id}.json`);
users.push(user);
});
console.log(users);
expected output
[
{
name: 'user 1',
city: 'abc'
},
{
name: 'user 2',
city: 'pqr'
},
{
name: 'user 3',
city: 'xyz'
}
]
So to launch in parallel:
const ids = await fetch('https://remote-server.com/ids.json');
const userPromises = ids.map(id => fetch(`https://remote-server.com/user/${id}.json`));
const users = await Promise.all(userPromises);
to launch in sequence:
const users = [];
const ids = await fetch('https://remote-server.com/ids.json');
for(const id of ids){
const user = await fetch(`https://remote-server.com/user/${id}.json`);
users.push(user);
}
You forgot to add async in the forEach:
ids.forEach(async (id) => { // your promise is in another function now, so you must specify async to use await
var user = await fetch(`https://remote-server.com/user/${id}.json`);
users.push(user);
});

Async-await 'Promise {<pending>} with Array.prototype.map

I know there are many questions discuss about the same error and I saw most of them and they didn't fix my problem.
I wrote this code:
const userOrganizationGroups = (organizationGroupsList) => {
if (Array.isArray(organizationGroupsList) && organizationGroupsList.length) {
const result = organizationGroupsList.map(async (element) => {
const { organizationId, groupId } = element;
const { Organizations, Groups } = models;
const organization = await Organizations.findOne(
{ _id: organizationId },
{ name: 1, _id: 0 },
);
const group = await Groups.findOne({ _id: groupId });
return Object.assign({}, {
organizationName: organization.name,
group: group.name,
});
});
return result;
}
};
when I debug the code:
console.log('userOrganizationGroups : ',userOrganizationGroups(list))
I got such a result:
userOrganizationGroups: Promise { <pending> }
I found a similair question: Promise { } - Trying to await for .map and I used the solution mentioned in the question:
const userOrganizationGroups = async (organizationGroupsList) => {
if (Array.isArray(organizationGroupsList) && organizationGroupsList.length) {
const result = await Promise.all(organizationGroupsList.map(async (element) => {
const { organizationId, groupId } = element;
const { Organizations, Groups } = models;
const organization = await Organizations.findOne(
{ _id: organizationId },
{ name: 1, _id: 0 },
);
const group = await Groups.findOne({ _id: groupId });
return Object.assign({}, {
organizationName: organization.name,
group: group.name,
});
}));
return result;
}
How can I fix this issue?
instead of
console.log('userOrganizationGroups : ',userOrganizationGroups(list))
use
userOrganizationGroups(list).then( groups => console.log('userOrganizationGroups : ', groups)
or
(async () => {
const groups = await userOrganizationGroups(list);
console.log('userOrganizationGroups : ', groups);
})();
console.log() was called first because you didn't wait using await or then.
So You should write below instead of
console.log('userOrganizationGroups : ',userOrganizationGroups(list))
;(async () => {
const resultGroups = await userOrganizationGroups(list)
resultGroups.forEach(group => {
console.log(`group: ${JSON.stringfy(group, null, ' ')}`)
})
})()

How to use the beforeEach in node-tap?

Can someone provide an example on how to use the beforeEach? http://www.node-tap.org/api/
Ideally, an example of the promise version, but a callback version example would also be nice.
Here is a test I created which works fine:
'use strict';
const t = require('tap');
const tp = require('tapromise');
const app = require('../../../server/server');
const Team = app.models.Team;
t.test('crupdate', t => {
t = tp(t);
const existingId = '123';
const existingData = {externalId: existingId, botId: 'b123'};
const existingTeam = Team.create(existingData);
return existingTeam.then(() => {
stubCreate();
const newId = 'not 123'
const newData = {externalId: newId, whatever: 'value'};
const newResult = Team.crupdate({externalId: newId}, newData);
const existingResult = Team.crupdate({externalId: existingId}, existingData);
return Promise.all([
t.equal(newResult, newData, 'Creates new Team when the external ID is different'),
t.match(existingResult, existingTeam, 'Finds existing Team when the external ID exists')
]);
});
})
.then(() => {
process.exit();
})
.catch(t.threw);
function stubCreate() {
Team.create = data => Promise.resolve(data);
}
Before I do anything, I want to persist existingTeam. After it's saved, I want to stub Team.create. After these two things, I want to start actually testing. I think it would be cleaner if instead of using a Promise.all or perhaps duplicating the test code, I could use beforeEach.
How would I convert this to use beforeEach? Or what is an example of its usage?
Simple, just return promise from callback function
const t = require('tap');
const tp = require('tapromise');
const app = require('../../../server/server');
const Team = app.models.Team;
const existingId = '123';
const existingData = {
externalId: existingId,
botId: 'b123'
};
t.beforeEach(() => {
return Team.create(existingData).then(() => stubCreate());
});
t.test('crupdate', t => {
t = tp(t);
const newId = 'not 123'
const newData = {
externalId: newId,
whatever: 'value'
};
const newResult = Team.crupdate({
externalId: newId
}, newData);
const existingResult = Team.crupdate({
externalId: existingId
}, existingData);
return Promise.all([
t.equal(newResult, newData, 'Creates new Team when the external ID is different'),
t.match(existingResult, existingTeam, 'Finds existing Team when the external ID exists')
]);
}).then(() => {
process.exit();
}).catch(t.threw);
function stubCreate() {
Team.create = data => Promise.resolve(data);
}

Categories

Resources