no-rxstate-subscriptions-outside-constructor
What it flags
RxState subscription methods (state.connect or state.hold) called outside the constructor or state-init callback. Subscribing from a click handler or lifecycle hook is usually a sign of a non-reactive approach; set the subscription up once where the component is initialized.
With functional rxState(), the setup callback passed to rxState() is the state-init equivalent of the constructor; connect calls belong there.
Options
allowedMethods (string[]): additional method names in which connect/hold are allowed, on top of the constructor (e.g. an Angular lifecycle hook).
// eslint.config.js (flat config)
import rxAngular from '@rx-angular/eslint-plugin';
export default [
{
plugins: { '@rx-angular': rxAngular },
rules: {
'@rx-angular/no-rxstate-subscriptions-outside-constructor': ['error', { allowedMethods: ['ngOnInit'] }],
},
},
];
// .eslintrc (legacy)
"rules": {
"@rx-angular/no-rxstate-subscriptions-outside-constructor": [
"error",
{ "allowedMethods": ["ngOnInit"] }
]
}
❌ Incorrect
@Component({
template: '<button (click)="handleClick()">...</button> ...',
})
class NotOkComponent {
constructor(
private service: SomeService,
private state: RxState<MyState>,
) {}
handleClick() {
this.state.connect('something', this.service.doSomething());
}
}
@Component({
template: '<button (click)="handleClick()">...</button> ...',
})
class NotOkComponent {
constructor(
private service: SomeService,
private state: RxState<MyState>,
) {}
handleClick() {
this.state.hold(this.service.doSomething());
}
}
✅ Correct
@Component({
template: '...',
})
class OkComponent {
private readonly service = inject(SomeService);
private readonly state = rxState<MyState>(({ connect }) => {
connect('data', this.service.getData());
});
handleClick() {
this.state.set({ clicked: true });
}
}
Custom configuration, correct when { "allowedMethods": ["ngOnInit"] } allows connect in ngOnInit:
@Component({
template: '...',
})
class OkComponent {
noDataFetching = input(false);
constructor(
private state: RxState<MyState>,
private service: SomeService,
) {}
ngOnInit() {
if (!this.noDataFetching()) {
this.state.connect('data', this.service.getData());
}
}
}
Why
The RxState-discipline rationale (set subscriptions up once at initialization) belongs to the RxState reactive discipline & effects concept.