Setting state in React - javascript

Here is sandbox with codehttps://codesandbox.io/s/setting-react-state-mxzkf?file=/src/App.js
We have React state:
const [valid, setValid] = useState({
nameOK: true,
nameValidated: false,
nameError: false,
emailOk: true,
emailValidated: false,
emailError: false
});
An input field and validating function:
function validateName(value) {
setValid({ ...valid, nameOK: false, nameValidated: true }); //WHY THIS DOES NOT RUN???
if (value.length > 4) {
setValid({...valid, nameOK: true, nameError: false});
} else {
setValid({ ...valid, nameError: true });
}
}
I would expect to nameValidated: true be set unconditionall with every validation,but it does not happen...
Why?

As Pascal mentioned you're using the current state and thus overriding your fist change. React does not merge properties in functional component state however. The way to do it is computing new state based on the previous state
setValid(valid =>{return { ...valid, property: newValue }});

Related

How to update Recoil JS Atom Object

I have an Atom object defined like this:
export const walletInfo = atom({
key: "walletInfo",
default: {
connected: false,
someString: "",
someInt: 0,
someOtherInfo: "",
},
});
The question is, how do I change only single value in Atom object without overwriting everything else? For example, how do I set connected: true and keep the rest of the information?
The code below will "erase" the rest of the object
import { walletInfo } from "../src/atoms";
const [wallet, setWallet] = useRecoilState(walletInfo);
setWallet({
connected: true,
});
You need to combine the previous state and updated state like this
setWallet((prevState) => ({
...prevState,
connected: true,
}));

How do lodash's isEqual returns true while comparing two objects that have functions in Javascript/React?

I am trying to understand why/how this comparison gets true? As I know, if there is/are function/s in objects it will never be equal because every time parent renders their references change. Why does this equal?
// _.isEqual comes from lodash
shouldComponentUpdate(nextProps){
const myQuestion = _.isEqual(nextProps, this.props) // this is getting true and my question for this line
return !_.isEqual(nextProps, this.props)
}
// this is my props
{
"dispatch": function(){},
"saveHeaderSettings": function(){},
"buttonFormat": "absolute",
"externalUpdateEnable": false,
"isEditModeAllowed": true,
"isVisiblePivotView": false,
"isWorkInSubsetEnabled": false,
"mode": 0,
"rowUpdateEnable": false,
"updateMode": 0,
"updateModes": {
"isVisibleCellUpdate": true,
"isVisibleRowUpdate": true,
"isVisiblePopupUpdate": true
}
}
PS: dispatch and saveHeaderSettings methods are coming from Redux's mapDispatchToProps

React setState of boolean value not updating

New to React, trying to update the state of an object where on property already has a set boolean value. However, it seems like the state is not updating.
I understand that state is update asynchronously, maybe that could coming into play here? I don't believe I can use the setState method that takes an object and callback function because I need access the the previous state.
Here is my initial state:
items: [
{
id: 0,
title: 'Dev Grub',
selected: false
},
...
]
And here is my event handler:
handleCardClick(id, card) {
this.setState((preState, props) => ({
[preState.items[id].selected]: [preState.items[id].selected] ? false : true
}));
console.log('new state: ', this.state.items[id].selected);
}
I've also tried this instead of the ternary: ![card.selected]
updating just a property at the second level of the state won't work. use something like below:
handleCardClick(id, card) {
let items = [...state.items];
items[id].selected = items[id].selected ? false : true
this.setState(() => ({
items
}));
}
React setState doesn't work this way, it doesn't update state right away but rather enqueues the change to update it at some point in the future.
If you want to do something as soon as the state has been updated you can use callback parameter
this.setState((preState, props) => ({
[preState.items[id].selected]: [preState.items[id].selected] ? false : true
}), () => console.log('new state: ', this.state.items[id].selected);)
See docs on setState
setState is async, you console log after the state has been changed like ths
handleCardClick = (id, card) => {
this.setState(
{
[this.state.items[id].selected]: [this.state.items[id].selected]
? false
: true,
},
() => console.log('new state: ', this.state.items[id].selected),
);
};

React : how to update states which have both mutable and immutable values

In Javascript, string, integer and boolean values are immutable, but objects and arrays are mutable.
How should we update states in React, if states have both types of values?
e.g.
constructor(props) {
super(props);
this.state = {
success: false,
error: false,
errorMessages: {}
};
}
Assuming that you need to upgdate all of the properties (success, error, errorMessages) at once, what would be best way to achieve it?
At least I'm sure that errorMessages shouldn't be updated directly, because it's mutable by nature, but what about the rest of them?
I tried something like the following, but this ends up in a wrong result.
const errorMessages = {
...this.state,
"errorMessages": error.response.data,
};
this.setState({
errorMessages,
success: false,
error: true,
});
//The errorMessages property will have "success" and "error" property in it
As long as you supply a new value for errorMessages, React will update the state correctly. You're not mutating state directly here, you're just providing a new value for the field, and React will do the necessary mutation:
this.setState({
errorMessages: error.response.data
success: false,
error: true,
});
So assuming your state is originally this
this.state = {
success: false,
error: false,
errorMessages: {}
};
And then you create a new object for your errorMessages like this
const errorMessages = {
...this.state,
"errorMessages": error.response.data,
};
this.setState({
errorMessages,
success: false,
error: true,
});
Then, your next state will kinda look like this, and I am unsure if this is what you want
{
errorMesages: {
success: false,
error: true,
errorMessages: {
// content of the error.response.data
}
},
success: false,
error: true
}
You probably wanted to assign the new state directly, which is in fact the errorMessages const you created, you are just over doing it ;)
The reason why this is so, is because when adding a variable to an object without a value, but just by name, javascript will automatically name the label the same as the variable, eg:
const a = 10;
const b = {
a
};
// result in: { a: 10 };
console.log(b);
There are 3 ways to update state:
this.setState({
success: !this.state.success,
error: !this.state.error,
errorMessages: delete this.state.id // if id were a prop in errorMessages
})
this.setState((prevState) => {
return {
success: !prevState.success,
error: !prevState.error,
errorMessages
}
});
this.setState((prevState) => {
return {
success: !prevState.success,
error: !prevState.error,
errorMessages
}
}, () => { // some callback function to execute after setState completes })

react wildcard in using setState

I have
this.state = {
modal_1: true,
modal_abc: true,
modal_special: true
}
how can I change everything that start with modal to false? is it possible with
this.setState({
`modal_*`: false
})
There is no such thing as wildcards in React's setState method or javascript's object literal. You can manualy iterate over object keys and reduce it, e.g.:
const newState = Object.keys(this.state).reduce((result, key) => {
// conditionally set value of result
result[key] = key.startsWith('modal_') ? false : this.state[key];
return result;
}, {});
// and set new state
this.setState(newState);

Categories

Resources