Refactoring mystery, or is it?

I am working in a Gatsby Reactjs project and I’ve recently come across something that has me confused. I solved the issue but I don’t understand why it works the way it does. Maybe someone can help me understand.

In a code block inside a class component I may have 3 or more value pairs in state. Now if I refer to just one key within the state as:


but the linter wants me to refactor that one reference of state, one cannot simply just write:

const something = this.state; 

If one does it brings all keys value pairs with their state into that variable assignment. Often that might not be a problem but when you specifically need just the one referenced state value, and you are being forced to refactor your code, I found the line has to be written:

 const { something } = { ...this.state };  

It works, though I don’t understand why? Thank you for any help :thinking:

I’m not certain exactly what the linter is tripping on, but generally direct references to state variables are only discouraged if you’re assigning, not if you’re simply referencing to capture a value. So

const something = this.state.something;

should be fine, whereas

this.state.something = something;

would not be. That being said, this would also be ok

const something = this.state.something;
something = somethingElse;

because even though something came from the state, the assignment here is to the variable, not a property of the state or some state-defined reference.

Using object spread ({...}) helps protect against this - to a degree - because it copies properties from one object to another. This expression

{ ...this.state }

creates a new object with copies of all the properties in this.state. This means any assignments to this object would not affect the original state because it would be modifying the object created in that expression instead.

const stateCopy = { ...this.state };
stateCopy.something = somethingElse; // does not affect this.state

However, if you’re using destructuring to capture a single value from this expression like so

const { something } = { ...this.state }; 

then this is effectively no different than saying

const something = this.state.something;

Both assign the local variable something to the same value - the value in this.state.something. The fact that the something property was copied into an intermediary object with a spread expression in the first variation does not change what that value is or how it behaves as a local variable.

Depending on the value of something, all of this may be inconsequential. Any primitive value in something would be considered safe since you wouldn’t be able to assign any property internal to that value. This would not be the case if something were an object value. If you were able to say

something.anotherProperty = anotherValue;

Then you run the risk of modifying the state without going through setState() which is a no-no. Object spread does not protect you against this either because it does not do a deep copy. It only copies direct properties of one object to another. It does not copy properties of properties meaning anotherProperty would be a reference to the same anotherProperty in this.state even if object spread was used to create something as seen in the earlier code sample.

[eslint] Must use destructuring state assignment (react/destructuring- 

Thank you @senocular Your explanation is thorough and very helpful. Thank you!

My .eslintrc was a bit of a nightmare to set up. I like to destructure my code but I wish the react/recommended did not error on a simple

const something = this.state.something;

Thanks again! :grinning:


Its an interesting eslint rule.

I think its purpose is mainly to enforce a style guide for your code, not to necessarily help prevent errors. Both always and never each warn when using the alternate style, suggesting either is acceptable technically, just a matter of preference for the author.

1 Like

I did read this eslint-plugin-react rule and I was hesitant to assign ‘never’. Now I have and I’m happier :smiley: Thanks!