Adding new array in object - javascript

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);
});
}

Related

How to push multiple objects into a React state variable

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.

How to iterate through an array and have it return only the contacts missing a certain object

I've been working on this for over an hour and have no clue what to include...
directions:
-Filtering Data.. The application's search feature allows users to filter contacts in various ways. The interviewer would like you to filter out those who do not have an Instagram account.
Using the given contacts array, save the contacts who do not have an Instagram account to a variable called 'noInstagram.' Don't just hard-code the answer into the variable but rather filter the contacts out of the array programmatically.
let contacts = [
{
name: "Jane Doe",
age: 21,
social_media: {
instagram: "jane.doe",
twitter: "jane_doe"
}
},
{
name: "John Doe",
age: 21,
social_media: {
instagram: "john.doe",
twitter: "john_doe"
}
},
{
name: "Mary Deer",
age: 21,
social_media: {
twitter: "mary_deer"
}
},
{
name: "Gary Deer",
age: 21,
social_media: {
twitter: "gary_deer"
}
}
]
How Im starting off.
let noInstagram = contacts.filter((contact) => {
if ( contact.social_media. ????){
console.log(contact)
}
})
Try this :
let noInstagram = contacts.filter(c => !c.social_media.instagram);
console.log(noInstagram);
The filter method take a callback function as parameter and return an array.
This array is filled with all the element of the inital array that return true after passing throw the callback function.
let noInstagram = contacts.filter(contact => contact.social_media.instagram === undefined);
or
let noInstagram = contacts.filter(contact => !contact.social_media.hasOwnProperty("instagram"));
You are going in a good direcction with a filter, please visit Array.filter docs
let noInstagram = contacts.filter((contact) => {
//Return true if instagram is not found
return !contact.social_media.instagram
})
As you see, you must return true or false inside the filter to be able to filter or not the current object.

Merge objects in javascript into one

My issue is that I have an initial object with data in the function. The function receives params with values that are into this initial object. I need to update the initial object every time with the data, which comes from params.
The code:
export function saveLocalStorage(params = {}) {
let state = {
firstName: '',
lastName: '',
role: '',
idToken: '',
auth: false,
id: '',
email: '',
phone: '',
organizationId: '',
lastVisit: '',
}
localStorage.setItem('donisi-new', JSON.stringify(state))
}
params have the same names as names in initial object, example:
saveLocalStorage({
firstName,
lastName,
role,
organizationId,
auth: true,
idToken,
lastVisit: moment(new Date()),
})
So, for example, the first time I received the first object with params, for example:
saveLocalStorage({
firstName: 'La La',
lastName: 'Bla Bla'
})
and second time I received object with params:
saveLocalStorage({
role: 'admin',
phone: '+111111111'
})
How to update the initial state and don't delete the values and only update them?
Thanks to everybody.
This is a function I use to merge 2 JavaScript objects:
function mergeObjects(obj, src) {
for (var key in src) {
if (src.hasOwnProperty(key)) obj[key] = src[key];
}
return obj;
}
So if you had these 2 objects:
var obj1 = {name: 'Bob', age: 30};
var obj2 = {name: 'Steve'};
And ran the function:
mergeObjects(obj1, obj2);
It would return:
{name: 'Steve', age: 30}
To achieve this behaviour you can use the ES6's spread operator (...) to merge objects. It will merge the two object. The new fields will be added from both object and existing ones will be updated.
Just replace your
localStorage.setItem('donisi-new', JSON.stringify(state))
with
localStorage.setItem('donisi-new', JSON.stringify({...state, ...params}))
The order of state and params is important here. This orders means state object will be updated with new values which exist in params object.
Part of the problem with updating initial state is you have state defined in the function, so those values can't be updated. One way to address this is to pull state out into its own file and then reference it in your function.
// state.js
export default {
firstName: '',
lastName: '',
role: '',
idToken: '',
auth: false,
id: '',
email: '',
phone: '',
organizationId: '',
lastVisit: '',
};
Then in your function you can reference and update it as necessary. The next time you call saveLocalStorage, the state will have been updated from the previous call.
import * as state from "./state.js";
export function saveLocalStorage(params = {}) {
/* Update state with values from params example
for (const [key, value] of Object.entries(params)) {
state[key] = value;
}
*/
localStorage.setItem('donisi-new', JSON.stringify(state))
}
I leave part of this in a comment because you may have something else in mind for updating state or before merging.

Adding an array and an object to an array react hooks

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.

Normalizr - is it a way to generate IDs for non-ids entity model?

I'm using normalizr util to process API response based on non-ids model. As I know, typically normalizr works with ids model, but maybe there is a some way to generate ids "on the go"?
My API response example:
```
// input data:
const inputData = {
doctors: [
{
name: Jon,
post: chief
},
{
name: Marta,
post: nurse
},
//....
}
// expected output data:
const outputData = {
entities: {
nameCards : {
uniqueID_0: { id: uniqueID_0, name: Jon, post: uniqueID_3 },
uniqueID_1: { id: uniqueID_1, name: Marta, post: uniqueID_4 }
},
positions: {
uniqueID_3: { id: uniqueID_3, post: chief },
uniqueID_4: { id: uniqueID_4, post: nurse }
}
},
result: uniqueID_0
}
```
P.S.
I heard from someone about generating IDs "by the hood" in normalizr for such cases as my, but I did found such solution.
As mentioned in this issue:
Normalizr is never going to be able to generate unique IDs for you. We
don't do any memoization or anything internally, as that would be
unnecessary for most people.
Your working solution is okay, but will fail if you receive one of
these entities again later from another API endpoint.
My recommendation would be to find something that's constant and
unique on your entities and use that as something to generate unique
IDs from.
And then, as mentioned in the docs, you need to set idAttribute to replace 'id' with another key:
const data = { id_str: '123', url: 'https://twitter.com', user: { id_str: '456', name: 'Jimmy' } };
const user = new schema.Entity('users', {}, { idAttribute: 'id_str' });
const tweet = new schema.Entity('tweets', { user: user }, {
idAttribute: 'id_str',
// Apply everything from entityB over entityA, except for "favorites"
mergeStrategy: (entityA, entityB) => ({
...entityA,
...entityB,
favorites: entityA.favorites
}),
// Remove the URL field from the entity
processStrategy: (entity) => omit(entity, 'url')
});
const normalizedData = normalize(data, tweet);
EDIT
You can always provide unique id's using external lib or by hand:
inputData.doctors = inputData.doctors.map((doc, idx) => ({
...doc,
id: `doctor_${idx}`
}))
Have a processStrategy which is basically a function and in that function assign your id's there, ie. value.id = uuid(). Visit the link below to see an example https://github.com/paularmstrong/normalizr/issues/256

Categories

Resources