1

I'm wondering if it's possible to destructure the properties/methods from an instance of a class or function while maintaining scope across the destructured variables? For example:

function Counter() {
  this.state = {count: 0}
  this.update = (fragment) => {
    this.state = Object.assign({}, this.state, fragment)
  }
  this.increment = () => {
    this.update({count: this.state.count + 1})
  }
  this.decrement = () => {
    this.update({count: this.state.count - 1})
  }
}

const counter = new Counter

const {
  state,
  increment,
  decrement
} = counter

console.log(state)
increment()
console.log(state)
decrement ()
console.log(state)

JS Bin

The result for each console.log is the same: {count: 0} because it's creating a new instance and new scope for each of the variables.

Is this intentional? Am I approaching this the wrong way? (I'm creating a library and I assume someone will try to use it this way so I want to make it work)

senornestor
  • 4,075
  • 2
  • 33
  • 33
  • It works as expected. "I assume someone will try to use it this way so I want to make it work" --- you better not. – zerkms Jan 06 '16 at 00:08
  • Actually this is a very good question about JS "basic" behavior. +1 – kaiser Mar 03 '20 at 07:18
  • https://stackoverflow.com/questions/45910018/how-to-bind-methods-when-destructuring-an-object-in-javascript – Bergi Oct 09 '22 at 17:46

2 Answers2

3

In your example, destructuring desugars to something like

const state = counter.state;

Assigning a new value to counter.state won't magically update the value of state. Destructuring doesn't change that.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
1

The object destructure is creating a local const state which is a memory reference to the current state object in the class instance.

const {
  state,
  increment,
  decrement
} = counter

The Object.assign in the update method is copying the contents of state + fragment into a new, empty object (at a new memory address...). this.state is then being set to that new object.

this.update = (fragment) => {
  this.state = Object.assign({}, this.state, fragment)
}

The destructured, local const state is not reactive - it will continue pointing to the object it was told to point to.

Rather than using a new, empty object in Object.assign, you can write directly to this.state. As well, you don't need the return value - Object.assign mutates.

this.update = (fragment) => {
  Object.assign(this.state, fragment)
}