React update state hook on functional component does not allow dot notation - reactjs

I'm getting the following error:
Parsing error: Unexpected token, expected ","
In my functional component (not class) I have:
const [ ministries, setMinistries ] = useState({
options: '',
selected: ''
});
later in another method I try to update ministries by doing the following:
let opts = [1, 2, 3, 4];
setMinistries({
ministries.selected: opts
})
Assuming ministries is the object and selected is in ministries I would expect dot notation.
ministries.selected: opts to work.
What am I doing wrong?

Please, be aware that the useState updater overwrite a previous state with a new one and it does not perform any merging.
Instead, it requires you to pass the complete state each time.
However, that's not the case with this.setState in a class component.
That's something that, to my advice, is important to remember to avoid subtle undesired behavior.
So, the correct way to update your state would be:
setMinistries(prevMinistries => ({
...prevMinistries,
selected: opts
}));

You can't do ministries.selected as an object key.
You can just do:
setMinistries({
selected: opts,
});

setMinistries({
ministries.selected: opts
})
This is bad syntax. You call the property of the object within an object.
Try
setMinistries({
ministries: {
selected: opts
}
})
or
setMinistries({
selected: opts
})

Related

arrow function use in setstate

I wrote a piece of code that I don't quite remember how it works, embarrassing to say.
The state is as below:
state={
api:[
{
id: 1,
purpose: "For the Android phone",
link: "playgoogle/GooglePlay",
status: "1"
},
{
id: 2,
purpose: "For the Android phone",
link: "playgoogle/GooglePlay",
status: "0"
}
]
}
And this is the snippet of code that I don't quite understand.
activateAPI=(id)=>{
this.setState(prev => ({
api: prev.api.map(api => api.id === id ? { ...api, status: 1 } : api)
}))
this.changeStatus(id,1);
}
I understand that a value is passed into the activateAPI as id but I don't quite understand how the prev and api: prev.api works here. I understand that the api inside the map is like a foreach where it checks whether the current item's id in the json array matches the function id then what does {...api,status:1} mean?
Any clarification would be much appreciated. Thank you for reading.
The nature of setState is async. What you've written is pretty clear that when you call setState with its callback signature. Internally once the callback completes, React returns the callback to setState. And here's what you're doing,
(state, props) => stateChange
Above is the true signature of the setState that React provides.
State is a reference to the component state at the time the change is being applied.
For more info link
Also when you use spread operator, it spreads the object and updates the prop that is changed. So when you do {...api, status:1}. It will compare the API object from the initial one and updates the status value to 1.
Remember this will be the shallow comparison. For updating nested state objects you need to go to the nested level to observe proper state update.
api: prev.api.map(api => api.id === id ? { ...api, status: 1 } : api)
That mean , When mapping array if it find the api with the id matched, it with return the new api object with property 'status' change to '1' . If not it will just return the old api object
sorry for my bad english :D hope it help
What the Spread operator does is that it creates a new object.
So in this case when you are doing {...api, status: 1} a new object is getting created with all the keys present in api object and then overriding the status value with 1 when api.id === id is true.
let x = {key: "Key", val: 1, val2: 22}
//spread and update the value, it'll only update `val` all other values wil be as is
console.log({...x, val: 2})
//the original object will be as is
console.log(x)
let y = {z: {key: "Key", val: 3}, z1: 2}
//assign y to y1 by spreading
let y1 = {...y}
y1.z.val = 4;
//z.val in both y and y1 will be modified because spread will not spread deep objects
console.log(y1)
console.log(y)
Coming to the arrow function in setState will accept a function as first parameter. React will pass state and props as params to the passed function. This way you will be able to access the state in order to update it by using old state.
Below is the setState function
setState(updater, [callback])
The first argument is an updater function with the signature:
(state, props) => stateChange
state is a reference to the component state at the time the change is being applied. It should not be directly mutated. Instead, changes should be represented by building a new object based on the input from state and props.
Hope this helps.

Better syntax to update nested objects on redux

Given a reducer example like the following
_({
expandAbility: (state, a: { which: string }) => ({
...state,
report: state.report && {
waitingForIt: false,
content: state.report.content && {
...state.report.content,
interaction: {
expandAbilities: !state.report.content.interaction.expandAbilities.contains(a.which)
? state.report.content.interaction.expandAbilities.add(a.which)
: state.report.content.interaction.expandAbilities.remove(a.which)
}
}
}
}),
})
(state type given below just for question context purposes)
const initialState = {
report: undefined as
| {
waitingForIt?: boolean
content?: {
supportedFightIds: number[]
deathCut: number
reportInfo: any
deathsFull: any
deathsByPlayer: any
deathsByAbility: any
interaction: {
expandAbilities: Set<string>
}
}
}
| undefined,
error: undefined as Error | undefined
}
Is there any kind of trick or "flavor-of-the-moment" library which would allow me to write a reducer update operation like expandAbility in a shorter way? (besides maybe creating some vars to reference inner paths)
There are lots of immutable update utilities out there, check out some options at https://github.com/markerikson/redux-ecosystem-links/blob/master/immutable-data.md#immutable-update-utilities and see what would be the best fit for you.
For starters check out Immutability-helper or immer.
So there are two things you could do to help simplify this. The first thing I like to do is move the logic out of the reducer and instead just pass in a value and say set expandAbilities to action. expandAbilities.
The second is actually something we do at work. We use immutableJS and wrote a single reducer that handles all of our state calls because you can give it a path of the parts of state that need to be updated and the value to update it with so we extracted that out and now it is easy to say dispatch(actions.update({path: ['report', 'content', 'interaction', 'expandAbilities'], value: '123' }))
You can even expand this so you can pass in a list of values that need to be updated and even preform validations around the data.

is this actually mutating state? A bad practice?? - react

this is a piece of code I came across and I am not sure what it is doing actually?
it feels to me like it is mutating the state
Version 1:
this.state.items = [...this.state.initialItems];
Now, is the above code mutating state??
And can the above also be written as Version 2:
this.setState({
items:[...this.state.initialItems];
});
the thing is that version 1 is working but version 2 isnt.
So,what's wrong here?
what am I missing?
what does ...this.state.initialItems do? is that the spread operator?
This code is definitely mutating state. Don't do it:
this.state.items = [...this.state.initialItems];
Yes, the ... is a spread operator in this context.
For version 2, make sure you don't use the semicolon.
It should just be :
this.setState({
items:[...this.state.initialItems]
});
And of course like the poster below has said, it's asynchronous so you won't see the results right away.
Augmentation 1:
If you want to do things after your state is ready, you can add a callback as your 2nd argument to setState.
this.setState({
items:[...this.state.initialItems]
}, ()=>{
// do stuff
}
);
Augmentation 2:
Your first argument to setState can be a function as well, as the other poster has mentioned, so that when you access state it will be the "freshest" copy, and not the one that was captured when you created the object that you passed to setstate:
this.setState((stateNOW) => {
return {items:[...stateNOW.initialItems]};
}, ()=>{
// do stuff
}
);
Version 1 is wrong. But the real question is Why?
state is a special object in React and it is not to be mutated.
Say you declare an object:
const obj = {
label: 'doe'
value: 'foo'
}
Then, you update the obj with
obj.value = 'bar'
This is the process of mutation and should never perform it with a state variable. Instead we use setState as a request to change the state.
... is the spread syntax.
As the other answers have mentioned, the reason why you think it is not working might be because state updates are asynchronous.
You have to use the setState callback or componentDidUpdate lifecycle hook to log the value of state.
setState(stateChange[, callback])
Another issue in the code is the use of this.state.something inside setState. Like we already mentioned before setState is asynchronous and a number of state updates may be merged. So using this.state.initialItems may not always work out for you, if you miss a update.
this.setState((prevState) => {
return {items: [...prevState.initialItems]};
});
This would be the right way to use a value of previous state.
Version 1 is mutating your state and this is a bad practice.
Version 2 is not and the reason why it's not working (actually it is) is that calls to setState are asynchronous and therefore you will not have access to its up-to-date value in the next line.
Should you need access to the new state value, you can use:
this.setState(prevState => ({
items: [...prevState.initialItems],
}), () => {
console.log(this.state) // new state value
})
Please also refer to this link in ReactJS docs about how to set state properly.
The ... is indeed the spread syntax.
You can use it, for example:
To add items to an array
const arr = [1, 2, 3, 4]
const newArr = [...arr, 5, 6] // This will spread everything in arr and add numbers 5 and 6 to newArr
console.log('arr', arr)
console.log('newArr', newArr)
To get items from an array
const arr = [1,2,3,4]
const [firstItem, secondItem, ...others] = arr
console.log('firstItem:', firstItem)
console.log('secondItem:', secondItem)
console.log('others:', others)
To create a new object
const obj = {
name: 'Stack Overflow',
age: 10,
}
const newObj = {
...obj, // gets all properties from obj
age: 12, // but only change age
}
console.log('obj', obj)
console.log('newObj', newObj)

React set state variable

I've an confusion. I'm trying to add array variable in setState. My code is working properly but wanted to confirm some doubt before committing my code.
Which is right way to store array in state variable ?
var names = ['Jake', 'Jon', 'Thruster'];
this.setState({
state: names
});
Or
this.setState((state) => {
state.items.push(names[0]);
return state;
});
What is necessary of return statement here ?
Can some one please explain me the difference here ? I searched in google but I'm still confused.
var names = ['Jake', 'Jon', 'Thruster'];
this.setState({
names //according to Airbnb rules
});
or
this.setState({
names: names
});
this.state.names = ['Jake', 'Jon', 'Thruster'];
setState takes a second argument - callback, that will called after setting State properties
setState({property1: value1, ...}, () => { //some code after State changed })
The first approach is much more common and in my opinion, easier to read. The issue with your code is you don't need to use the key state, because the function you are calling is "setting state". The key should be something like firstNames.
this.setState({
firstNames: names
});
You could also just pass the object in the function like this because setState takes an object as a parameter.
var namesObject = {
firstNames: names
}
this.setState(namesObject);
I would read more about it here and keep doing small tutorials on this an you'll get the hang of it.
https://facebook.github.io/react/docs/react-component.html#setstate

React: Array and Immutability Helpers

I am trying to add an array to a state that contains an array. I wish to add the data to the end of the array. My data looks like this:
getInitialState: function() {
return {name: [{program: "File Manager"},
{program: "Add File"},
{program: "Index"},
{program: "File Editor"}],
program: "Index",
display: true};
},
and I wish to update the name variable to also contain something. For example add {program: "Text Editor"}
How could I go about doing this with this.setState()? Or, if not, is there another way to do this. I read about $push but couldn't really find any examples that helped me out. I wish to do this in componentDidMount:
Create a new array and assign it to the state:
var currentName = this.state.name;
var newName = currentName.concat([{program: "Text Editor"}]);
this.setState({name: newName});
(The first line isn't necessary, just included for clarity.)
You could also use the function version of setState:
this.setState(function(state) {
return { name: state.name.concat([{program: "Text Editor"}]) };
});
If you're in an environment that supports ES2015 arrows, you can do this in a pretty concise way:
this.setState(state => ({ name: state.name.concat([{program: "Text Editor"}]) });
Since concat already generates a new array, you don't need to use the immutability helpers, but if you want to, it'd look like this:
var newState = update(this.state, { // generate a new state object...
name: { $push: [ "TextEditor" ] } // with the item pushed onto the `name` key
});
It's possible to mutate an existing property of this.state and then re-set it with setState, e.g.
// warning: antipattern!
this.state.name.push("Text Editor");
this.setState({name: this.state.name});
but this is not considered a very good practice (you should always avoid directly modifying this.state):
Notes:
NEVER mutate this.state directly, as calling setState() afterwards may replace the mutation you made. Treat this.state as if it were immutable.
You can read the this.state.name array, modifying it (by pushing to the array), then call setState again.

Resources