setState not updating the array in react - javascript

I am working on a react app where I have to change keys of objects in an array.Here's my code:
getInterfaces(){
//assign a array to interfaceOption state
let interfaceOptions=[
{
"interface_name": "ORU",
"interface_id": "1"
},
{
"interface_name": "MLM",
"interface_id": "2"
},
]
//change interfaceOptions keys to title and value
let interfaceOptionsWithTitle = interfaceOptions.map(function(item){
return {
title: item.interface_name,
value: item.interface_id
}
})
console.log(interfaceOptionsWithTitle)
this.setState({
interfaceOptions: interfaceOptionsWithTitle
})
//print updated interfaceOptions
console.log(this.state.interfaceOptions)
}
Here I initially assign a array,then I change its keys and print it using console.log and it prints the updated array but when I then setState the array and console log it again,it returns a empty array.Can someone help me to understand this?
Will this work if I want to update interfaceOptions?
this.state.interfaceOptions = this.state.interfaceOptions.map(item => {
return {
title: item.interface_name,
value: item.interface_id
};
});
Thanks

Becasue state only has new value when component re-render. So just put console.log to outside function getInterfaces to see the new value
getInterfaces(){
...
}
console.log(this.state.interfaceOptions)

setState is asynchronous in nature, you are trying to update the value and printing it in next line which will always give you the old state value because at the time when the browser tries to print the value the state is not updated, it takes some time to update the value.
If you want to print the updated state value you have pass a callback function to setState and print the value
e.g
this.setState({
interfaceOptions: interfaceOptionsWithTitle
},() => {
console.log(this.state.interfaceOptions)
})
Note: The callback function in the above setState will be executed after the rendering is completed.

Related

Update state value of single object in an array

I have list of items where I want to display a loader and hide it upon completing certain action.
For example, here is my array items
[
{
"id": "69f8f183-b057-4db5-8c87-3020168307c5",
"loading": null
},
{
"id": "30d29489-0ba9-4e00-bc28-8ad34ff1a285",
"loading": true
},
{
"id": "5f54ebbd-d380-4a54-bb1d-fc6c76dd1b72",
"loading": false
}
]
I am adding item to array with loading value as null the reason is. I want to process as soon as the the state is updated, hence I am using useEffect hook to observe for any change, if any new item with loading value null is added, then I proceed for action.
My problem is, when I try to modify a single loading value to false, it gives me weird behaviour and set all loading value to false.
What I want to have it, when I change the loading value of a single item in array, then the UI should re-render only for the changed item.
If you want to have a look at fiddle with working example, here is the link https://codesandbox.io/s/d8lh4-d8lh4
Where am I going wrong here?
It's simple use this code:
setTimeout(() => {
setItems((existingItems) =>
existingItems.map((item) =>
item.id === newItem?.id ? { ...item, loading: false } : item
)
);
}, 2000);
Looking at your code, I think the issue is related to accessing the wrong value of newItem and items in setTimeout. Both of them can be solved by doing something similar to the one below.
const handleUpload = newItem => {
// set loading to false to new item after 2 seconds
setTimeout(
theNewItem => {
setItems(exisitingItems =>
exisitingItems.map(item =>
item.id === theNewItem.id ? { ...item, loading: false } : theNewItem,
),
);
},
2000,
newItem,
);
};
You have [items] dependency in your useEffect, which is calling setItems in loop in your handleUpload function.

set an array with useState doesn't change the result

I have a list of data like this:
data = [
{
id:1,
name: "boo"
},
{
id:1,
name: "boo"
}
]
I initialize it with react UseState like follows:
const [items, setItems] = useState(data)
And I map this in my react code and visualize them.
If I want to delete one I do it with the following function:
const deleteItem = async (id) => {
console.log('BEFORE- - - ->', items);
// this functions calls an api to delete the item
await deleteItem({ id });
setItems((oldItems) =>
oldItems.filter(({ id }) => id !== commentId)
);
console.log('AFTER- - - ->', items);
};
Graphically the component with the deleted item disappears, but actually the second console.log prints the same thing as the first log. In this case if I delete the element with id:1 I will get the same log while I see graphically that the element disappears. This causes a problem with indexing if I want to delete more than one item.
Which do you think is the problem? has I to do it into one useEffect?
Setting state is an asynchronous action. If you put a console outside of that function, on the next re-render you will see your state updated correctly (just like you've seen your item disappear visually).
This happens because setState is asynchronous so the state really changes after you console.log

Add key/value pair to existing array of objects

I have an array of objects that is saved into a userList useState which is composed of:
[{
firstName: "blah"
lastName: "blah2"
}
{
firstName: "test"
lastName: "test2"
}]
I have a useEffect that calls a function and returns a value. I want to store a new key and value to each user in userList.
useEffect(() => {
userList.forEach((user, index) =>
returnNewValueForNewKeyFunction(user, index).then(newValue => {
userList[index]['newKey'] = newValue
//this console.log shows new field and value
console.log(userList)
//this console.log ALSO shows new field and value
console.log(JSON.stringify(contactList[index]))
})
)
}
}, [])
This is fine if I'm operating out of console.log, but unfortunately I need to render the data onto the page.. in my render I have:
return (
<TableBody>
{userList
.map((user, index) => (
<TableRow>
<TableCell>
{user.newKey}
</TableCell>
)
user.newKey is showing as blank and it seems like the user wasn't updated at all. How can I make it so the value is actually updated and can be read from when rendering?
You shouldnt mutate your list, you should use useState to store your list, so something like this :
const [ state, setState] = useState(userList);
Then when you want to update, do something like this :
const listCopy = [...state];
//Logic to update your list here
listCopy[index][otherindex] = Value;
setState(listCopy)
Hope this helps
You are modifying your userList but not calling your set function on which means React won't know to re-render with the updated state.
Instead of mutating the current state, you should create a new array and then call the set function returned by useState with the updated array after making your changes.
It also looks like your returnNewValueForNewKeyFunction is a promise / async which means each of your item changes are happening async. You'll need to make these synchronous / wait for them all before updating your state to make your state change a single update for the UI.
E.g., putting these both together - if you are doing:
const [userList, setUserList] = useState();
You could do:
useEffect(() => {
// Since can't use an async func directly with useEffect -
// define an async func to handle your updates and call it within the useEffect func
const updateUsers = async () => {
// Create a new array for your updated state
const updatedUserList = [];
// Loop over your values inline so your can await results to make them sync
for (let index = 0; index < userList.length; index ++) {
const user = userList[index];
const newVal = await returnNewValueForNewKeyFunction(user, index);
// Create a shallow copy of the original value and add the newValue
updatedUserList[index] = { ...user, newKey: newValue };
// ... Any other logic you need
}
// Call set with the updated value so React knows to re-render
setUserList(updatedUserList);
};
// Trigger your async update
updateUsers();
}, [])

useState won't update state

I'm trying to update a state using useState hook, however the state won't update.
const handleSelect = address => {
geocodeByAddress(address)
.then(address => {
const receivedAddress = address[0];
const newAddress = {
street: receivedAddress.address_components[1].long_name,
number: receivedAddress.address_components[0].long_name,
city: receivedAddress.address_components[3].long_name,
country: receivedAddress.address_components[5].long_name,
zip: receivedAddress.address_components[6].long_name,
selected: true
};
handleAddressSelection(newAddress);
})
.catch(error => console.log(error));
};
When handleSelect is called, it creates the object newAddress, and then calls handleAddressSelection passing newAddress.
function handleAddressSelection(newObj) {
console.log(newObj);
Object.keys(newObj).forEach(function(key) {
setValues({ ...values, [key]: newObj[key] });
});
}
In console.log(newObj) the object is filled fine, with all the data I need. Then I call setValues for each object in newObj, however no matter what, the values object won't receive the new data. The only one that is updated is selected: true, all others won't update.
What should I do to fix it?
You're calling setValues multiple times in a loop, and every time you do so, you spread the original values, and thus overwrite anything that was done on the previous setValues. Only the very last setValues ends up working, which happens to be the one for selected: true
If you need to base your update on the previous value of the state, you should use the function version of setValues, as in:
Object.keys(newObj).forEach(function(key) {
setValues(oldValues => ({ ...oldValues, [key]: newObj[key] }));
});
But even better would be to only call setValues once. If you're calling it multiple times, then you're going to generate multiple renders. I'd do this:
setValues(oldValues => ({...oldValues, ...newObj}));
Values is not even defined anywhere in your examples. My guess is, it's some cached copy and you should be using callback variant of the state setter instead:
setValues(previousValues => ({ ...previousValues, [key]: newObj[key] }));

How to initialize from setstate in Reactjs?

_handleClickFilter(value, xname, chartId){
console.log("dataSourceId", this.state.dataSource);
this.setState({
filterData: [{
filter: "equals",
value:value ,
attribute:xname,
}]
});
let filterdefinitions = {
dataSourceId : "59ef50d6e4b054efd6d8aa53",
filterDefinitions: this.state.filterData,
}
let data = {
filterDefinitions: [filterdefinitions],
};
DashboardAction._ApplicableFilterToDashboard(data, this.props.params.dashboardId);
DashboardAction._ApplicableFilterToChart(data, this.props.params.dashboardId, chartId);
DashboardAction._saveFilterToDashboard(data, this.props.params.dashboardId);
}
I am able to get values in the setstate which I want. But the values are not getting set. Showing the values exists in the this.state only.
thanks in advance
setState is async, so it's not guaranteed that the state will be set after you have used it until the re-render is triggered. You should be very careful about using the state right after you have set it, a more 'reactive' approach is usually better. However, if you want to make sure that you'll be able to access the new state, you can use the second argument of setState which is a callback function that will be called when the state is set.
You can use it this way:
_handleClickFilter(value, xname, chartId){
this.setState({
filterData: [{
filter: "equals",
value:value ,
attribute:xname,
}]
}, () => {
let filterdefinitions = {
dataSourceId : "59ef50d6e4b054efd6d8aa53",
filterDefinitions: this.state.filterData,
}
let data = {
filterDefinitions: [filterdefinitions],
};
DashboardAction._ApplicableFilterToDashboard(data, this.props.params.dashboardId);
DashboardAction._ApplicableFilterToChart(data, this.props.params.dashboardId, chartId);
DashboardAction._saveFilterToDashboard(data, this.props.params.dashboardId);
});
}
Replace your set state code with this
this.setState({ 
filterData: this.state.filterData.map((data, index) => {
data.filter = "equals",
data.value = value,
data.attribute=xname
}) });

Categories

Resources