AlexZeDim
AlexZeDim

Reputation: 4352

Private props return unmodified or losing values within the class inside scope of child class method

So, I have two classes service-class & entity-class with private constructor & static async method, for creating new instance of class. My service-class create & call new entity-class instance and modified it with its properties.

Example | This is NOT NEST.JS code:

class Service {
   run() {
     const entity = Entity.createNewEntity(args);

     await entity.methodMother();

     const getMyProps1Back = entity.props1();

     // is [ ] empty array, not modified after methodChild
     console.log(getMyProps1Back);

   }
}

Each method of my entity is covered with bind decorator from npm: bind-decorator module

class Entity {
  this.props1! Array<Record<Type>>;
  this.props2 = new Map([]);


  private constructor(someArgs: any) {
     // some stuff
     this.props1 = [];  
  }

  get getProps1() {
    return this.props1;
  }

  static async createNewEntity() {
     const entity = new Entity();
     // blah-blah work here
     return entity;
  }

  @bind
  public async methodMother() {
    this.props1.push({ some: stuff });

    // 

    this.methodChild();
    // method logic
  }

  @bind
  private methodChild() {
     // adding more elements, or overwriting the array
     this.props1 = [test1, test2, test3];
  }
}

So my questions here are about explaining why context is lost during calling of methodChild within the methodMother and how is my getter return an unmodified array of props1?

## How does it work, and what should I do to avoid it?

Passing an array like argument and returning it back, like this? Or something else, like makign them anonymous?

  @bind
  private methodChild(props1 : Array<OfProps1>) {
     // adding more elements, or overwriting the array
     props1 = [test1, test2, test3];

     return arg;
  }

Upvotes: 1

Views: 204

Answers (2)

KetaJson
KetaJson

Reputation: 129

What you are looking at needs an explanation aimed at both ideas:

  • The clock is not actually misplaced inside methodChild. This has been taken care of by the @bind decorator. That can't be the problem.

  • The real problem is in how you're modifying props1 in methodChild:

    this.props1 = [test1, test2, test3];

    And this line transfers the value of the original props1 array to a brand new Replacement an array, not just changing the existing one

  • Meanwhile, your getter getProps1 is likely returning a reference to the original props1 array, which hasn't been modified:

    get getProps1() { return this.props1; }

    This getter was defined before methodChild ran, so it's still pointing to the original (empty) array.

To fix this, you have a few options:

  • Modify the existing array instead of reassigning:

    @bind
    private methodChild() {
      this.props1.length = 0;  // Clear the array
      this.props1.push(test1, test2, test3);  // Add new elements
    }
    
  • Update the getter to always return the current value of props1:

    get getProps1() {
      return this.props1;
    }
    
  • If you really have to reassign props1 then use a method instead of a getter.

    getProps1() {
      return this.props1;
    }
    

    Then call it asentity.getProps1() instead of entity.props1.

As for your suggestion of passing the array as an argument:

@bind
private methodChild(props1: Array<OfProps1>) {
  props1 = [test1, test2, test3];
  return props1;
}

This wouldn't solve the problem because JavaScript passes arrays by reference. Reassigning the parameter props1 inside the method won't affect the original this.props1. You'd need to do something like:

@bind
private methodChild() {
  const newProps1 = [test1, test2, test3];
  this.props1.length = 0;
  this.props1.push(...newProps1);
}

This modifies the existing array in place, ensuring that all references to this.props1 see the changes.

Upvotes: -1

Hopeful
Hopeful

Reputation: 96

The code you've sent seems to work fine without the @bind decorator (After some clean-up):

type KeyType = any
type ValueType = any

class Entity {
  props1!: Record<KeyType, ValueType>[];
  props2 = new Map([]);


  private constructor() {
    // some stuff
    this.props1 = [];
  }

  getProps1() { // <-- NOTE: Removed the `get` from here
    return this.props1;
  }

  static async createNewEntity() {
    const entity = new Entity();
    // blah-blah work here
    return entity;
  }

  public async methodMother() {
    this.props1.push({ some: 'stuff' });

    this.methodChild();
  }

  private methodChild() {
    this.props1 = [
      ...this.props1,// existing values

      { some: 'other stuff' },
      { evenMore: 'other stuff' },
      { andMore: 'other stuff' }
    ]
  }
}

And when you run it:

class Service {
  async run() {
    const entity = await Entity.createNewEntity();

    await entity.methodMother();

    const getMyProps1Back = entity.getProps1();

    console.log(getMyProps1Back);
  }
}

You get a nice Output:

[
  {
    some: "stuff",
  },
  {
    some: "other stuff",
  },
  {
    evenMore: "other stuff",
  },
  {
    andMore: "other stuff",
  }
]

This is based on the code you have given.

Upvotes: 3

Related Questions