I have a state variable that stores an object which contains the array invitees as a value:
const [values, setValues] = useState({
date: '',
location: '',
invitees: []
})
I have an array containing multiple objects:
users = [
{ _id: 'id1', name: 'name1', email: 'email1' },
{ _id: 'id2', name: 'name2', email: 'email2' },
{ _id: 'id3', name: 'name3', email: 'email3' }
]
I'd like to be able to push each of these objects into the values.invitees array.
I can do this for individual objects using a function like this:
const addInvitee = (user) => {
setValues({ ...values, invitees: [...values.invitees, user] })
}
But my problem is when I try to add multiple objects at once.
I have tried mapping through them like this:
users.map((user) => {
addInvitee(user)
})
And also using a for loop:
for (let i = 0; i < users; i++) {
addInvitee(users[i])
}
However both these attempts result in only the last object element in the array being added.
Is anyone able to help me understand why this is happening and what I can do to fix it. Many thanks.
Problem
That is because values reffered inside the addInvitee is the same initial values (empty array) not the updated values.
const addInvitee = (user) => {
setValues({ ...values, invitees: [...values.invitees, user] })
}
Solution
To avoid the stale value issue you should use the callback function of setter of the useState hook.
Try like below:
const addInvitee = (user) => {
setValues((prevValues) => ({
...prevValues,
invitees: [...prevValues.invitees, user]
}));
};
In this case, prevValues is always guaranteed to be the updated values.
Related
I'm working on an application that involves a somewhat complex user registration. Something similar to this.
const [data, setData] = useState({
identity: '',
people: [{
name: '',
address: [{
street: '',
city: ''
}]
}]
})
function addAddress(){
setData({
...data,
people: [
...data.people,
{
address: [
...data.people[0].address,
{
street: '',
city: ''
}
]
}
]
})
}
When a user adds a new address to a person he is registering, he should add a new address to the person and keep the previous data. But it creates a new data array with just the address data, outside of the specified person.
Could someone help me how to do this insertion into the array?
It's not the best solution i guess, but it should work
I'm using here JSON.stringify() and JSON.parse() to deep copy your previous data
function addAddress (newAddress) => {
setData((previousData) => {
let newData = JSON.stringify(previousData);
newData=JSON.parse(newData);
newData.people[0].address.push(newAddress);
return (newData);
});
}
I'm trying to add an array and an object to an array.
This is my constructor
const [dashboard, setDashboard] = useState({ loading: true, data: [], address: '' })
and this is how I wanted the data to end up
{
loading: true,
data: [{id: 0, name: 'A'}, {id: 1, name: 'B'}],
address: 'xxx'
}
I can't seem to figure it out and was only able to manage to add the arrays but not the other objects like loading and address with something like this but this is not what I need and I'm just giving an example of what I tried doing:
the constructor of this one in the bottom is different from what I want to use, I used something like this
const [dashboard, setDashboard] = useState([])
setDashboard((prev) => {
return [...prev, newData]
})
If I understand your problem correctly you are trying to do something like this:
setDashbaord(prevDashboard => ({ ...prevDashboard, address: "xxx", data: [...prevDashboard.data, newData] }));
Is that what you are looking for?
const [dashboard, setDashboard] = useState({ loading: true, data: [], address: '' })
The line above looks great, no problems there.
setDashboard((prev) => {
return [...prev, newData]
})
Doing this will set your dashboard variable to be an array, and is definitely not what you said you wanted.
setDashboard((prev) => {
return {
...prev,
data: [...prev.data, ...newData] // <-- assuming new data is an array
}
})
Depending on what newData looks like you may need to manually merge the data into your previous data object. For instance, if you want to ensure you have no duplicate values in the array, or you need to sort them.
I am trying to edit this initial state:
const initialFilters = {
PGfenced: '',
PGgrill: '',
PGrestrooms: '',
PGstaffed: '',
PGtoddler: '',
Animals: '',
};
I have this so far and it just adds another object onto the end. I want to edit and replace just part of the initialFilters.
const [playgroundFilters, setPlaygroundFilters] = useState([initialFilters]);
const updateItem = (whichvalue, newvalue) => {
setPlaygroundFilters({ ...playgroundFilters, [whichvalue]: newvalue });
console.log(playgroundFilters);
};
onPress={(value) => updateItem('PGfenced', value)}
options={[
{ label: 'No Fence', value: '' },
{ label: 'Partially', value: 'Partially fenced' },
{ label: 'Fully', value: 'Fully fenced' },
]}
Your state variable is an array, but then you assign an object to it. I assume you want it to be an object since you're updating it with a new value, just remove the square brackets in the state declaration like this:
const [playgroundFilters, setPlaygroundFilters] = useState(initialFilters);
And then you will be able to update that object the way you did.
UPDATED: Thanks to everyone for the help
I'm getting 2 collections from Firestore: Roles and Apps. Apps is a subcollection of Roles so it is contained inside of Roles collection, therefore, to get the apps I need to get the Roles document where they are contained first. I have no problem doing this but I need to save them as they are stored in firebase to group them later, I can't figure out how. I want some data like this:
[
{
roleDescription: 'System Administrator',
active: true,
roleId: '1'
apps: [
{
appId: '2',
appDescription: 'Manage Roles',
groupId: '1',
route: 'roles',
appName: 'Roles',
active: true
},
{
appId: '1',
appDescription: 'Users',
groupId: '1',
route: 'users',
appName: 'Users',
active: true
},
{
...
}
]
},
{
active: true,
roleId: '2',
roleDescription: 'Employee',
apps: [
{
appId: '5',
appDescription: 'Upload Data',
groupId: '1',
route: 'roles',
appName: 'Roles',
active: true
},
{
appId: '1',
appDescription: 'Users',
groupId: '1',
route: 'users',
appName: 'Users',
active: true
},
{
...
}
]
}
]
Currently I have this code where I can get all the roles in snapshot and map them to get every role individually and then with snapshot2 get the apps that are contained inside that role also individually to assign every app in snapshot2 to to the object or array of roles contained also in an array.
Here is my code:
ref.get()
.then(function(snapshot) {
return Promise.all(snapshot.docs.map(doc => {
var AppRole = {};
AppRole.role = doc.data();
roles.push(AppRole);
return doc.ref.collection('apps').get()
.then((snapshot2) => {
return snapshot2.docs.map(app => {
roles[count].apps = app.data(); // Here I need to push app.data() to roles[count].apps and problem solver but I don't know how to do it (roles[count].apps is an object and I know it doesnt have the push method but I tried with a counter also like roles[count].apps[i] = app.data(); i++; but no success )
})
})
console.log(count);
count++;
}))
.then(() => {
console.log(roles);
res.render("pages/roles", { name: req.session.name, idiom: req.session.idiom, roles: roles, apps: apps, pageInfo: req.session.lang.roles});
})
You should be able to do this without any counters. I've commented the main changes in the code.
Basically, just keep a reference to the role after you create it. Then you can assign a list of apps to it all at once by using map().
const roles = []; // You can add and remove elements to a const array
const db = admin.firestore();
const ref = db.collection('roles');
ref.get()
.then(function(roleQuery) {
return Promise.all(roleQuery.docs.map(roleDoc => {
// ** Create a new role and keep a reference to it **
const role = roleDoc.data()
roles.push(role);
return roleDoc.ref.collection('apps').get()
.then((appQuery) => {
// ** Easily create a new array by calling
// ** map() on the app documents
role.apps = appQuery.docs.map(appDoc => {
return appDoc.data();
})
// ** alternate syntax:
// ** appQuery.docs.map(appDoc => appDoc.data())
})
}))
.then(() => {
console.log(roles);
res.render("pages/roles", { name: req.session.name, idiom: req.session.idiom, roles: roles, apps: apps, pageInfo: req.session.lang.roles});
})
Don't really understand the full scope of your code, but why not just create an object that assigns apps to roles, and store the end result in the roles array?
You shouldn't need the j counter in this instance. I would imagine it looks something like this:
let roles = [];
const count = 0;
const db = admin.firestore();
const ref = db.collection('roles');
ref.get()
.then(function(querySnapshot) {
return Promise.all(querySnapshot.docs.map(doc => {
var AppRole = {}; //Declare AppRole object
AppRole.role = doc.data(); //Based on your question, this should return one role
roles.push(AppRole); //Adds object to array (You can use 'roles[count] = AppRole' if you feel more comfortable)
return doc.ref.collection('apps').get()
.then((snapshot2) => {
return snapshot2.docs.map(app => {
roles[count].apps = app.data(); //Based on your question, this should return an array of apps. That array should be stored within that particular object.
})
})
console.log(count);
count++;
}))
.then(() => {
console.log(roles);
res.render("pages/roles", { name: req.session.name, idiom: req.session.idiom, roles: roles, apps: apps, pageInfo: req.session.lang.roles});
})
I have an indexed list of users in the JS object (not array). It's part of the React state.
{
1: { id: 1, name: "John" }
2: { id: 2, name: "Jim" }
3: { id: 3, name: "James" }
}
What's the best practice to:
add a new user { id: 4, name: "Jane" } with id (4) as key
remove a user with id 2
change the name of user #2 to "Peter"
Without any immutable helpers. I'm using Coffeescript and Underscore (so _.extend is ok...).
Thanks.
This is what i would do
add: var newUsers = _.extend({}, users, { 4: { id: 4, ... } })
remove: var newUsers = _.extend({}, users) then delete newUsers['2']
change: var newUsers = _.extend({}, users) then newUsers['2'].name = 'Peter'
If you're not using Flux, you use this.setState() to update the state object.
delUser(id) {
const users = this.state.users;
delete users[id];
this.setState(users);
}
addChangeUser(id, name) {
const users = this.state.users;
users[id] = {id: id, name: name};
this.setState(users);
}
Then you can execute your test cases with this:
addChangeUser(4, 'Jane);
addChangeUser(2, 'Peter');
delUser(2);
Apart from _.extend you can use Map for storing user
let user = new Map();
user.set(4, { id: 4, name: "Jane" }); //adds with id (4) as key
user.myMap.set(2, { id: 2, name: "Peter" }); // set user #2 to "Peter"
user.delete(3); //deletes user with id 3
setState also accepts a function, which you might find more intuitive
function add( user ) {
this.setState( users => {
users[ user.id ] = user
return users
}
}
function remove( id ) {
this.setState( users => {
delete users[ id ]
return users
}
These functions assume that your state object is your users object, if it is actually state.users then you'd have to pick users out, passing a function to setState will always be called passing the actual state object.
In this example add can also be used to amend, depending on your actual use-case you may want to create separate helpers.
Using spreads:
Adding
this.setState({
...this.state,
4: { id: 4, name: "Jane" },
}
Removing id 2
let prevState = this.state;
let {"2": id, ...nextState} = prevState;
this.setState({
...nextState,
}
Changing id 2
this.setState({
...this.state,
2: {
...this.state["2"],
name: "Peter",
}
}