update nested state object with array inside - reactjs

I am having trouble updating my state object that has an array of objects inside. There are few things that I have tried but none of them worked. Here is an example of my state object. With the example below, I want to update the values for test and test2 depending on whatever the user types in input field. Can someone guide me in the right direction. Thank you!
this.state = {
someProperty: {
someOtherProperty: [
object in array: {
test: true,
test2: false
},
object in array: {
test: true
test2: false
}
..
]
...
}
...
}
My attempt on updating the value inside the object is as follows:
this.setState(prevState => ({
...prevState,
someProperty: {
...prevState.someProperty,
someOtherProperty: [
...prevState.someProperty.someOtherProperty,
test: false
]
}
}))

You can create an array latestSomeOtherProperty, update the wanted index and then;
const latestSomeOtherProperty = someOtherProperty.map(prop=>{...prop, test: false}); // it updates all but you can put condition here
this.setState(prevState => ({
...prevState,
someProperty: {
...prevState.someProperty,
someOtherProperty: [
...latestSomeOtherProperty
]
}
}))

Related

Finding an array item in state

I have the following array in my Sate:
this.state = {
currentAsset: null,
assets: [
{ id: uuidv4(), selected: false, text: 'Sorte',},
{ id: uuidv4(), selected: false, text: 'optical_dillusion2322.jpg'},
{ id: uuidv4(), selected: false, text: 'man_read_abook3242332.jpg'},
]
}
I want to find one of these assets and assign it to currentAsset. I have the following function:
handleAssetIDChange(assetID) {
console.log(assetID)
var currentAsset = this.state['assets'][assetID]
console.log(currentAsset)
// this.setState({
// currentAsset: this.state['assets'][assetID],
// openAssetSideBar: true
// }, () => {
// console.log(this.state['assets'][assetID])
// })
}
You can see the commented out part is now working. I am trying to set the currentAsset and then trigger the open of the sidebar to display the contents, but currentAsset is not getting set.
I have the assetID, how can I locate the one I need? Another question i have is in many stackoverflow posts they reference state vars like objects, aka:
this.state.assets but I always get an error when trying to do that.
How can I assign currentAsset and then trigger the openAssetSidebar when it has been set?
I would use a filter use get the object, this should update your currentAsset in the state.
handleAssetIDChange(assetID) {
console.log(assetID)
var currentAsset = this.state['assets'].filter( item => item.id === assetID)[0]
console.log(currentAsset)
this.setState(( prev ) => {
...prev,
currentAsset,
openAssetSideBar: true
})
}
In order to find the asset that you are looking for with a specific id, instead of
var currentAsset = this.state['assets'][assetID]
You should do:
const foundAsset = this.state['assets'].find((el) => el.id === assetID)
then to update the state, you would do:
this.setState((prev) => ({...prev, currentAsset: foundAsset}))

React: setState - which is more correct?

I have a react class component that has a state object and I want to update the object with setState. I can get the state to update correctly two different ways and would like to know if one is more correct than the other.
this.state = {
people: {
name: "",
typeofComponent: "class",
}
};
onChange = e => {
// option 1
this.setState((prevState) => ({
people: {
...prevState.people, name: e.target.value
}
}));
// option 2
this.setState({ people: { name: e.target.value }});
}
Your second option would work if the state has only 1 field (name in your case). If you set that way it will overwrite the whole people. That's why we need to use spread operator inorder to make sure the other states are not lost
let a = {
people: {
name: "",
typeofComponent: "class",
}
}
let newName = 'newname'
let withSpread= {people:{...a.people,name:newName}}
let withoutSpread = {people:{name:newName}}
console.log(withSpread)
console.log(withoutSpread)

How can I update a nested state with arrays in React?

I have a nested array, and I am trying to update some properties, but i don't know the syntax in react to do that.
this.state = {
databasesList: {
name: 'Databases',
toggled: true,
children: [
{
name: 'OneDatabase',
children: [
{ name: 'collection1' },
{ name: 'collection2' }
]
}
]
}
}
I am trying to update with this, but it does not work:
this.setState({ this.state.databasesList.children[0].children: newData })
To set nested state in React you should use JS spread operator so in your example it should be something like this:
this.setState((prevState) => ({
...prevState,
databasesList: {
...prevState.databasesList,
children: {
...prevState.databasesList.children[0],
children: {
newData,
},
},
},
}));
If you want to use lodash set here's a pretty nifty solution.
https://twitter.com/stwilz/status/1092958989208317952
But if you just want to use set on it's own this would be the way to go.
const yourFunction = newData => this.setState(state => set(state, ['databasesList', 'children', 0, 'children'], newData));
The spread operator works fine as well. It just gets super verbose when you have a huge state object.

How do I concat two objects into one - React-Native state

Here are two objects one is creating onload of page while another is I want to add on button click into the current state
constructor(props){
super(props);
this.state = {
marked: null,
tempMarkedDates: [],
}
this.toggle = this.toggle.bind(this);
}
// First object into marked is like
console.log(JSON.stringify(this.state.marked));
Output ::
[
{
"2019-01-02":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2019-01-04":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2019-01-08":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2018-12-29":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2018-11-27":{
"disabled":true,"selected":true,"selectedColor":"#fc3232"
},
}
]
Working from my own answer here on stack overflow
//Now I am creating new object like
day = "2019-01-10"
let obj = {};
obj[day] = {
disabled: true,
selected: true,
selectedColor: '#fc3232'
}
//So the output will be
[
{
"2018-12-30":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
}
}
]
I want to merge both the object and update this.state.marked
And it should be remain object after concatenation, while my code is changing it into array
Desire Output - Need to join last or first date object of 30-12-2018
[
{
"2019-01-02":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2019-01-04":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2019-01-08":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2018-12-29":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2018-11-27":{
"disabled":true,"selected":true,"selectedColor":"#fc3232"
},
"2018-12-30":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
}
}
]
So need your help to concat the both object for React native state.
We can use the assign() method of javascript.
Reference: https://stackoverflow.com/a/51143342/3449578
let newstate = Object.assign(obj , this.state.marked[0])
this.setState({marked: newstate });
You can use ... spread operator inside this.setState:
var obj = {
"2019-01-02":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2019-01-04":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2019-01-08":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2018-12-29":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
},
"2018-11-27":{
"disabled":true,"selected":true,"selectedColor":"#fc3232"
},
}
console.log(obj)
var s = {...obj,
"2018-12-30":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
}
}
console.log(s)
In React you can do this by:
this.setState({ marked: {...this.state.marked, "2018-12-30":{
"disabled":true,"selected":true,"selectedColor":"#10cc74"
}})
This is how I would do it in react. Keep your whole state and update the marked with new values using setState.
Just provide values to the handleMarked function to update the state. And delete dummy values for day and value.
constructor(props) {
super(props);
this.state = {
marked: {},
tempMarkedDates: [],
}
this.toggle = this.toggle.bind(this);
}
handleMarked = (day, value) => {
/* Provide values to function params above and delete dummy values below */
day = '2019-01-10';
value = {
disabled: true,
selected: true,
selectedColor: '#fc3232'
}
this.setState({
...this.state,
marked: {
...this.state.marked,
[day]: value
}
},
() => console.log(JSON.stringify(this.state.marked))
);
}

setState in map function of react

My Requirement is to update the state value in map function of componentWillReceiveProps.
In console log all I am getting is 1s but sub.subscribed contain 0s and 1s
Reference of console window: http://prntscr.com/jqifiz
constructor(props) {
super(props);
this.state = {
regionAll: [],
};
}
componentWillReceiveProps(nextProps){
if(nextProps.apiData !== false ){
nextProps.apiData.data.datacenter.category.map((sub)=> {
console.log(sub.subscribed,'sub.subscribed');
this.setState({
regionAll: [
...this.state.regionAll,
sub.subscribed
]
},()=>{
console.log(this.state.regionAll,'sub');
})
})
}
Is this a correct way to update state in reactjs?
setState is async.In Array#map, it called multiple time.Only last value is added in array regionAll and not all because of async setState call with multiple value.
You can collect all sub.subscribed value in single array with Array#reducer then perform state update.
if (nextProps.apiData !== false) {
let sub = nextProps
.apiData
.data
.datacenter
.category
.reduce((accum, sub) => [
...accum,
sub.subscribed
], [])
this.setState({
regionAll: [...sub]
}, () => {
console.log(this.state.regionAll, 'sub');
})
}
The problem arises because setState calls are batched and you are updated React state based on prevState, you should instead use functional state for such cases
componentWillReceiveProps(nextProps){
if(nextProps.apiData !== false ){
nextProps.apiData.data.datacenter.category.map((sub)=> {
console.log(sub.subscribed,'sub.subscribed');
this.setState(prevState => ({
regionAll: [
...prevState.regionAll,
sub.subscribed
]
}),()=>{
console.log(this.state.regionAll,'sub');
})
})
}
However its a bad idea to call setState in a map, you can instead get the data from map and call setState just once like
componentWillReceiveProps(nextProps){
if(nextProps.apiData !== false ){
const subscribed = nextProps.apiData.data.datacenter.category.map((sub)=> {
console.log(sub.subscribed,'sub.subscribed');
return sub.subscribed;
})
this.setState(prevState => ({
regionAll: [
...this.state.regionAll,
...subscribed
]
}),()=>{
console.log(this.state.regionAll,'sub');
})
}

Resources