ngrx / platform

Reactive State for Angular
https://ngrx.io
Other
8.05k stars 1.98k forks source link

Ngrx component state is undefined after setting default through the constructor #4546

Closed liesahead closed 1 month ago

liesahead commented 1 month ago

Which @ngrx/* package(s) are the source of the bug?

component-store

Minimal reproduction of the bug/regression with instructions

After upgrading ngrx from v16.2 to v18.0.2 there are a couple places in the app where component store state is undefined even in respective angular component ngOnInit method, while docs are stating this

Initializing through the constructor makes the state immediately available to the ComponentStore consumers

This was working as stated in docs before the upgrade.

Sample code:

public constructor() {
    super({
        loaded: true,
    } as any);

    console.error('constructor state value: ', this.state());
}

Worked like this image

Now it's behaving like this.. image

Expected behavior

State should never return undefined after component store constructor has been called with default state..

Versions of NgRx, Angular, Node, affected browser(s) and operating system(s)

Node v18.20.3

NgRx "@ngrx/component-store": "^18.0.2", "@ngrx/effects": "^18.0.2", "@ngrx/operators": "^18.0.2", "@ngrx/store": "^18.0.2",

Tested in these browsers: Firefox: 131.0 (64-bit) Edge : Version 129.0.2792.65 (Official build) (64-bit)

Angular CLI: 18.2.6 Node: 18.20.3 Package Manager: yarn 4.5.0 OS: win32 x64

Angular: 18.2.6 ... animations, cdk, cli, common, compiler, compiler-cli, core ... forms, language-service, material, platform-browser ... platform-browser-dynamic, router

Package Version

@angular-devkit/architect 0.1802.6 @angular-devkit/build-angular 18.2.6 @angular-devkit/build-ng-packagr 0.1002.4 @angular-devkit/core 18.2.6 @angular-devkit/schematics 18.2.6 @schematics/angular 18.2.6 rxjs 7.8.1 typescript 5.5.4 zone.js 0.15.0

Other information

No response

I would be willing to submit a PR to fix this issue

timdeschryver commented 1 month ago

Please provide a minimal reproduction for this, I've tried to reproduce this but in my case the state was always defined. This will help us to troubleshoot and fix the issue.

liesahead commented 1 month ago

@timdeschryver , I made some investigations and bug happens only in a single place amongst many of the apps that we have. This wasn't present in major 16 version, but can't test it with 17 as we updated 2 major versions (together with angular) at once. Every other place works like a charm, but in one specific place it fails.

I tried to create a reproduction but with no success so far (I will try to dedicate some more time to it). However, I fiddled with it a bit and can at least try to explain in what case it happens. So, we have a complex form like this (pseudo-code):

public form = new FormGroup({
    name: new FormControl<string>(''),
    palettes: new FormArray([
        new FormGroup({
            paletteId: new FormControl<string>(''),
        }),
        ...
    ]),
});

Then in the template we have multiple ngIfs on containers and then inside we iterate through the form array controls. But even when I moved it out of the loop - still failed, here's the pseudo-code when moved out of the loop (state init fails):

<item-with-store [control]="form.controls.palettes.controls[0].controls.paletteId" />

BUT, if we use some higher-level form control like name in our case, it works like expected (state init success):

<item-with-store [control]="form.controls.name" />

BUT, it fails if we wrap it like this which is the biggest mystery for me currently (state init fails):

@if (form.controls.palettes.controls[0]) {
    <item-with-store [control]="form.controls.name" />
}

Update: found out that this happens specifically with first item of the array no matter what, still trying to figure out why. Even if I disable rendering the first item of the array then the same bug will happen with the first rendered item.

liesahead commented 1 month ago

Played a bit more with this, still a mystery to me.

image

State is undefined after both actions. patchState should throw an error if state is not initialized, but it does not, however the state is still undefined after patching it.

image

image

markostanimirovic commented 1 month ago

When reporting a bug in the NgRx repo, it's required to provide a reproduction via the StackBlitz playground or GitHub repo.

Feel free to provide a reproduction, otherwise we'll have to close the issue.

Thanks!

liesahead commented 1 month ago

@markostanimirovic , ok, I will try one more time tomorrow.

Thanks for the patience, just tried to give as many details as I have at the current moment.

markostanimirovic commented 1 month ago

I'm going to close this issue. Feel free to reopen it with provided reproduction via StackBlitz playground or GitHub repo.