React how to delete properties from state - reactjs

I'm trying to replace whole state or at least delete all properties from it.
Before react 16 I just called these two lines
this.state = {}
this.forceUpdate()
With update to react 16, this didn't work anymore.
Currently I have this workaround in shouldComponentUpdate Method:
for (let prop in nextState) {
if (nextState.hasOwnProperty(prop)) {
delete nextState[prop];
}
}
But this 'feels' not right. So does anybody know the right way to reset whole state with new object? As far as I tested this.setState just changes the differences and leave other properties untouched.

You can't remove properties from the state, because it internally uses a merge
nextState = Object.assign({}, nextState, partialState);
So no way of removing already present keys. Only thing you can do is set the current keys to undefined.
If you know the properties, you can set them manually.
If not, you can try this to set them all to undefined:
this.setState(
Object.keys(this.state).reduce((a, c) => {
a[c] = undefined;
return a;
}, {})
)

Related

React nested state object not updating

I am trying to update a nested state object (checkedObjects) in a react class component, to track when checkboxes are checked and unchecked. checkedObjects has the following structure:
checkedObjects: {
[assignmentName]: boolean,
}
verifyObjects is a local variable that checks if the new name property was actually received. When I console out the contents of these objects however, checkedObjects is empty, while the new property was added to verifyObjects (see screenshot below). Can anyone advise why the state variable checkedObjects is not updating immediately?
Screenshot:
Code Snippet:
this.state = {
checkedObjects: {},
};
incrementCount(totalCount, id, checked, assignmentName) {
console.log("increment: totalCount", totalCount, " ; id:", id, checked);
// If a checkbox is clicked with an assignment name store on the checkedObjects
if (assignmentName) {
let verifyObjects = { ...this.state.checkedObjects };
verifyObjects[assignmentName] = checked;
this.setState(prevState => {
let tempObj = {...prevState.checkedObjects}
tempObj[assignmentName] = checked;
return {checkedObjects: tempObj}
});
console.log("SelectedAssignmentsObj:", this.state.checkedObjects);
console.log("VerifiedObject:", verifyObjects);
} //if
}
State updates don't occur instantaneously. When we call the setState() function, it schedules a state update. Try console logging tempObj to see the value that is being put inside of this.state.checkedObjects.
In short, your code is working the way it should but you wont be able to see the state update right after calling this.setState() [because the state update is scheduled and didnt happen at that instant]. If you want to ensure that your state did update the way you wanted, can add a dummy button on the side that console logs the value of this.state.checkedObjects or you can use the chrome extension React Developer Tools to find out the values in the state object.

Is it ok to modify part of prevState inside setState?

Is there a real disadvantage to modifying part of prevState and returning that part inside setState() ?
Example:
this.setState(prevState => {
prevState.myObject.isItTrue = !prevState.myObject.isItTrue;
return {myObject: prevState.myObject};
});
Rather than:
this.setState(prevState => {
const myObject = Object.assign({}, prevState.myObject);
myObject.isItTrue = !myObject.isItTrue;
return {myObject: myObject};
});
Is there any real disadvantage to the first code where I save myself the Object.assign() ?
EDIT: If I am correct, prevState.myObject is simply a reference to this.state.myObject, so changing prevState.myObject actually changes this.myObject.object as well! However, this doesn't seem to break anything, as long as I use setState() to pass an object that contains the new data, even if it's just a reference to the old objects inside this.state.
Do you agree that this is still ok, i.e. it won't break anything to do it like this?
Following documentation:
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.
https://reactjs.org/docs/react-component.html
So you should not apply changes directly to that state.
Either way, why not do something like this?:
this.setState(prevState => ({
myObject : {
...prevState.myObject,
isItTrue: !prevState.myObject.isItTrue,
}
}));
This way will get all the elements from the prevState but also change all the ones you want to modify.
First prevState and the this.state are the same object. So you are modifying the actual state directly.
Secondly, down the road you might pass the myObject as a prop to another component, and since it will be the same object always that component will not know that something has changed and it will not re-render (for example PureComponent and ones that implement componentDidUpdate or shouldComponentUpdate and test for changes)
See https://codesandbox.io/s/zen-aryabhata-m07l4 for showcases of all issues.
So you should use
this.setState(state => ({
myObject: {
...state.myObject,
isItTrue: !state.myObject.isItTrue
}
}));

When changing one specific state setting, is it necessary to restate all the other ones?

Let's say, I have a state that looks as follows.
constructor(props) {
super(props);
this.state = {
setting_a: "value-1",
setting_b: "color-green"
}
}
When I change the state of a specific setting (e.g. setting_a), I don't want other settings (e.g. setting_b) to disappear. So I also specify the other settings while changing the state. (which is easy using the spread operator ...state).
this.setState( {...this.state, setting_a: "value-2"});
I noticed though, that some tutorials restate them, and others only specify the changed key-values.
Things got just a little bit more complicated since the introduction of the Component#getDerivedStateFromProps method, ( since React 16.3 ).
static getDerivedStateFromProps(props, state) {
const oldSetting = state.setting_a;
const newSetting = props.setting_a;
if (oldSetting !== newSetting) {
// this is a very similar situation.
return ({ ...state, state.setting_a: props.setting_a});
}
return null;
}
Again, in the above example, I add all previous settings (i.e. ...state), because I don't want the other settings to be removed.
In both these cases, the same question: do I need to specifically repeat values which are already in the state ? Or are the states always merged incrementally, without removing ?
You don't need to copy the state (using spread operator or any idea) when updating the state with setState. The setState method updates the required state only:
this.setState( {setting_a: "value-2"});
So, now you will still get:
state = {
setting_a: "value-2",
setting_b: "color-green"
}
Similarly, it works like that when you return the object in getDerivedStateFromProps. The returned value is applied in the state without mutation.
You only need to copy the state when you want to update the property of state. For eg.:
// initial state
this.state = {
settings: {
a: 'value-1',
b: 'color-green'
}
}
Now, we have a and b property in settings state. So now, if you wanted to update the a, then you'll need to copy the settings:
this.setState((state) => ({settings: {...state.settings, a: 'value-2' } }))
The preceding example is with settings object. You can think similar with array of state. You may just do a google search for how to update the array without mutation?
It depends.
In your first case you could do:
this.setState( prevState => {
prevState.setting_a = "value-2";
return prevState
});
Or just go with:
this.setState({ setting_a: "value-2" });
As per React Docs State Updates are Merged.

React Component state value not updated, when setState is called on jsonObject's property

I am working on three properties of JSON object which returns boolean values.
updateChange=(value) =>{
//Making copy of existing json Object
let newState = JSON.parse(JSON.stringify(this.state.mainValue));
// chaning property of cellular over here
newState.cellular = value;
console.log(newState);
this.setState({mainValue:newState});
//I tried setState in many different ways. But Component is not changing state value
console.log(this.state.mainValue)
};
I found why this is happening. I am using getDerivedStateFromProps in this component to look at changes made by parent to update values. This is avoiding changes made to state of the child. So I have created a state called previous state to compare the previous props and present props from parent to render. This avoids component to refresh when ever it local state values changes.
static getDerivedStateFromProps(propsnow,state){
if(state.previous !== propsnow.detailSwitches){
return{
previous :propsnow.detailSwitches,
cellular: propsnow.detailSwitches.cellular,
wifi1: propsnow.detailSwitches.wifi1,
wifi2: propsnow.detailSwitches.wifi2
};
}
return null;
}
Any examples or better practices can be helpful. Thanks
You are ONLY setting the 'mainValue' variable of your state to the value of newstate, shouldn't you be updating the whole state?
this.setState({...newState})
You can try this
this.setState(prevState => ({
mainValue: {
...prevState.mainValue,
cellular: value
}}))
It is an immutable way to update state.

Best practice for updating newState via shouldComponentUpdate?

I've created a text input that only updates it's own state when you're typing via onChange, and then works it's way up the parent via onBlurEvent to prevent re-rendering too much of my GUI when a user is typing.
This works, but my drawback is that since the input has it's own state, if the props change because of external forces, and I want update my text input to be those new props, I'm in a bit of a pickle. From what I've been looking up, shouldComponentUpdate can create a fix, and my code works, but just because it works doesn't mean that my implementation is good.
When using react, I understand that we should not update the state by doing:
this.state.value = "This is a new value"
and instead do:
this.setState({
value: "This is a new value"
});
But what about directly changing newState which technically isn't "this.state" inside of shouldComponentUpdate?
I have the below logic:
shouldComponentUpdate(props, newState) {
if (...logic 1 ... && props.value != newState.value) {
//keep newState
return true;
}
else if (...logic 2 ... props.value != newState.value)
{
newState.value = props.value; //Right Here
return true;
}
return false;
};
That seems just as bad as (if not worse than) setting this.state to me. You should only ever be updating your state through setState, as you're running the risk of later updates overwriting your changes otherwise.
A much better solution would be to use the componentWillReceiveProps(nextProps) lifecycle hook - the docs say:
If you need to update the state in response to prop changes (for example, to reset it), you may compare this.props and nextProps and perform state transitions using this.setState() in this method.
Which sounds exactly like what you're trying to do!
I don't know your logic, so I can't give a full example, but here's a starting point:
componentWillReceiveProps(nextProps) {
if (/* your condition */ && nextProps.value != this.state.value) {
this.setState({
value: nextProps.value
});
}
}

Resources