Skip to content

rerender does recreate the component breaking ngOnChanges #360

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
shaman-apprentice opened this issue Jan 22, 2023 · 6 comments
Closed

rerender does recreate the component breaking ngOnChanges #360

shaman-apprentice opened this issue Jan 22, 2023 · 6 comments

Comments

@shaman-apprentice
Copy link

shaman-apprentice commented Jan 22, 2023

Bug

First of all of course thx for all the great open source work done! :)

Expected behavior

GIVEN a component which depends on previous value within ngOnChanges
WHEN calling rerender with new input properties
THEN ngOnChanges gets called with previous and new current value.

Actual behavior

The component gets recreated and changes within ngOnChanges are always initial changes.

Minimal reproduction

The second expect in the test below will fail.

@Component({
  selector: 'atl-fixture-2',
  template: `{{
    hasImproved
      ? 'Great, I cannot remember any result as good as yours! Keep throwing!'
      : 'You can do better than that... Try again!'
  }}`,
})
class MotivatorComponent implements OnChanges {
  @Input() dice = 1;

  hasImproved = false;

  ngOnChanges(changes: SimpleChanges): void {
    console.log(changes.dice);
    if (!changes.dice.firstChange && changes.dice) {
      this.hasImproved = changes.dice.currentValue > changes.dice.previousValue;
    }
  }
}

test('"ngOnChanges" gets called with an update', async () => {
  const { rerender } = await render(MotivatorComponent, {
    componentInputs: { dice: 1 },
  });

  expect(screen.getByText('You can do better than that... Try again!')).toBeInTheDocument();

  await rerender({
    componentInputs: {
      dice: 2,
    },
  });

  expect(screen.getByText('Great, I cannot remember any result as good as yours! Keep throwing!')).toBeInTheDocument();
});

The two console logs will be:

console.log
    SimpleChange {
      previousValue: undefined,
      currentValue: 1,
      firstChange: true
    }

      at MotivatorComponent.ngOnChanges (tests/rerender.spec.ts:75:13)

  console.log
    SimpleChange {
      previousValue: undefined,
      currentValue: 2,
      firstChange: true
    }

The actual console logs in a small demo app are:

{previousValue: undefined, currentValue: 1, firstChange: true}
{previousValue: 1, currentValue: 2, firstChange: false}
shaman-apprentice added a commit to shaman-apprentice/angular-testing-library that referenced this issue Jan 22, 2023
shaman-apprentice added a commit to shaman-apprentice/angular-testing-library that referenced this issue Jan 22, 2023
shaman-apprentice added a commit to shaman-apprentice/angular-testing-library that referenced this issue Jan 22, 2023
@shaman-apprentice
Copy link
Author

I was so free to prototype a possible solution in #361. Feedback or just copy pasting it for a possible solution is very welcome :)

@timdeschryver
Copy link
Member

@shaman-apprentice this is intended, rerender destroys the component and creates a new instance.
I think what you're looking for is change.

https://testing-library.com/docs/angular-testing-library/api#change

@shaman-apprentice
Copy link
Author

Thanks a lot for the fast reply and pointing me to the docs ❤️ To be honest, I haven't looked at the docs. I just assumed by my React knowledge... 👼 😞

In accordance to my React knowledge I find it natural to assume, that rerender does, what change does. However I am not sure if it is just me.^^

Although the docs are not clear to me as well:

Re-render the same component with different props. Input properties that are not defined are cleared. This calls detectChanges after the props are updated.

What do you think about making the docs more clear, that the component will be destroyed and created from scratch? E.g.

Re-creates the component with new props. This will call ngOnchanges with initial change object.

@timdeschryver
Copy link
Member

@shaman-apprentice We're always open to contributions to make things better, including the docs 😊
Feel free to open a PR to make the docs better (in this codebase using the TSDoc comments and/or in the docs repository https://github.com/testing-library/testing-library-docs/)

I've choosen change with the idea to make it clear that it involves a change detection from Angular instead of recreating the component. We're also open to rename either methods to something better if it's currently confusing.

The proposal seems good to me.
We can also mention how ngOnChanges is invoked for changes.

@shaman-apprentice
Copy link
Author

Cool :) I'll add this to my personal ToDo's (I'll probably have time to create a PR sometime between next week and the week after). Thanks again for your support and openness

@timdeschryver
Copy link
Member

No problem, thank you!
I'm also closing #361

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants