Adding an array and an object to an array react hooks - javascript

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.

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.

exclude already existing items from array in React

I am facing a challenge. I have 2 tables in firebase that I need to merge into 1 array. The only thing is that items are the same, they have to be removed from the array. So if an item already exists in an array, it should no longer be added to the array. So at the moment I get to see the items twice. Is there a possibility to prevent this? My function looks like this:
fetchItems(){
this.setState({
personalItems:[],
inactiveItems:[],
sprintItems:[],
})
let ref = Firebase.database().ref('/sprints/1/items/');
ref.on('value' , snapshot => {
snapshot.forEach((childSnap) => {
let state = childSnap.val();
console.log('firebase output:'+state)
var newelement = {title: state.title, author: state.author,user: state.user, public: state.public, x: state.x, y: state.y, key: state.key, match: state.match, notes: state.notes, status: state.status};
this.setState(prevState => ({
personalItems: [...prevState.personalItems, newelement],
}));
console.log('firebase'+state.name);
});
})
let refInactive = Firebase.database().ref('/users/'+this.state.user+'/items/');
refInactive.on('value' , snapshot => {
snapshot.forEach((childSnap) => {
let state = childSnap.val();
var newelement = {author: state.author, title: state.postit, status: state.status, key: state.key, user: state.user, match: state.match, x: state.x, y: state.y, public: state.public, notes:state.notes };
this.setState(prevState => ({
personalItems: [...prevState.personalItems, newelement],
}));
});
})
}
My database looks like this:
So you see that these items have the same key. These are also identical to each other. However, if 1 has already been added, this is sufficient, now they are both added.
Here is how you can merge an array into another without duplicates.
for(arr1Element of array1){
if(!array2.some(arr2Element => arr2Element.id === arr1Element.id)){
array2.push(arr1Element)
}
}
You could use a Set, which doesn't let you add duplicate values.
So the easiest you could achieve is something like:
const yourFinalArray = [...new Set([...arr1, ...arr2]);
Hope this helps.

Updating child array in reducer using React Context

I am doing some filtering using React Context and I am having some difficulty in updating a child's array value when a filter is selected.
I want to be able to filter by a minimum price, which is selected in a dropdown by the user, I then dispatch an action to store that in the reducers state, however, when I try and update an inner array (homes: []) that lives inside the developments array (which is populated with data on load), I seem to wipe out the existing data which was outside the inner array?
In a nutshell, I need to be able to maintain the existing developments array, and filter out by price within the homes array, I have provided a copy of my example code before, please let me know if I have explained this well enough!
export const initialState = {
priceRange: {
min: null
},
developments: []
};
// Once populated on load, the developments array (in the initialState object)
// will have a structure like this,
// I want to be able to filter the developments by price which is found below
developments: [
name: 'Foo',
location: 'Bar',
distance: 'xxx miles',
homes: [
{
name: 'Foo',
price: 100000
},
{
name: 'Bar',
price: 200000
}
]
]
case 'MIN_PRICE':
return {
...state,
priceRange: {
...state.priceRange,
min: action.payload
},
developments: [
...state.developments.map(development => {
// Something here is causing it to break I believe?
development.homes.filter(house => house.price < action.payload);
})
]
};
<Select onChange={event=>
dropdownContext.dispatch({ type: 'MIN_PRICE' payload: event.value }) } />
You have to separate homes from the other properties, then you can apply the filter and rebuild a development object:
return = {
...state,
priceRange: {
...state.priceRange,
min: action.payload
},
developments: state.developments.map(({homes, ...other}) => {
return {
...other,
homes: homes.filter(house => house.price < action.payload)
}
})
}

how do I add an item on array of Objects on setState in React?

I am creating arrays of objects and storing it on variables like this:
const nameOption = nameOptions(listHotels);
const estadoOption = stateOptions(listHotels);
const cityOption = cityOptions(listHotels);
my state is currently like this:
selectFilter: [
{ id: 1, type: 'Name'},
{ id: 1, type: 'Estado'},
{ id: 1, type: 'Cidade'},
],
I want to add these variables in a property called "options", like so:
selectFilter: [
{ id: 1, type: 'Name', options: nameOption},
{ id: 1, type: 'Estado', options: estadoOption},
{ id: 1, type: 'Cidade', options: cityOption},
],
how do I do it using the immutable react way?
First store the values to be inserted into a plain object, where the properties match the type values of selectFilter :
const options = {
Name: nameOptions(listHotels),
Estado: stateOptions(listHotels),
Cidade: cityOptions(listHotels)
}
Then merge that with selectFilter into the extended version of it:
this.setState(prevState => ({
selectFilter: prevState.selectFilter.map(filter =>
({...filter, options: options[filter.type]})
)
}));
NB: there is probably a more efficient way to build the options object, since you seem to iterate listHotels for each property. This could probably be done in one sweep using reduce. But without details about what these functions (nameOptions, stateOptions, ...) do there is not much I can offer for that. Look into calling listHotels.reduce.
You can use Object.assign() to duplicate your state into an new object. This object is now mutable. Once done modifying it, you then replace your entire state with the new version. Here is an example of how that could be done.
handleChange= () => {
let mutableState = Object.assign({}, this.state);
mutableState.thingToChange = foo;
this.setState(mutableState);
};
this.setState(({selectFilter}) => (
[
{...selectFilter[0], options: nameOptions},
{...selectFilter[1], options: estadoOptions},
{...selectFilter[2], options: cityOptions},
]
);

Iterating trough array and sending http request dynamically

Okay, I have this array pokemonColor=['name1', 'name2', 'name3']. How can I iterate trough this array and dynamically send http request so I can update state like this below? Because this what I am trying does not work, it creates a JavaScript object, but too many of them...
This is what I want:
this.state.pokemon:[
{name: name1, img: img1},
{name: name2, img: img2},
{name: name3, img: img3}
]
And this is how I am trying:
componentDidUpdate() {
// console.log(this.state.pokemonColor);
this.state.pokemonColor.forEach(pok => {
axios.get(`https://pokeapi.co/api/v2/pokemon/${pok}/`).then(res => {
// console.log(res.data.sprites.front_shiny);
this.setState({
...this.state,
pokemon: {
name: res.data.name,
img: res.data.sprites.front_shiny
}
});
});
});
// console.log(this.state.pokemon);
}
First of all - this.state.pokemon = [...] mutates the state directly. Your app may crash or at least you will receive a long, red error in your console.
Secondly - you don't have to destructure state inside setState function.
Thirdly - with every axios get request, you are overwriting pokemon field in your state. I'd suggest you to keep your pokemons inside array.
this.setState((prevState) => ({
pokemon: [
...prevState.pokemon,
{
name: res.data.name,
img: res.data.sprites.front_shiny,
},
],
});
Then you will be able to access your pokemons by referencing to this.state.pokemons, which will be an array of objects.

Categories

Resources