What is causing the subscriber to receive the error message "Cannot access 'variable' before initialization" while referencing itself?

Within my Angular 8 project, I have a component that is dependent on a service for data loading. It waits for a notification from the service signaling that it has finished loading in the following manner:

constructor(public contentService: ContractService) {
    let self = this;
    let sub = contentService.loaded.subscribe(function(loaded) {
        if (loaded) {
            self.loading = false;
            if (sub) {
                sub.unsubscribe();
            }
        }
    });
}

This setup functions correctly most of the time, but occasionally an error message surfaces:

ERROR ReferenceError: Cannot access 'sub' before initialization
    at SafeSubscriber._next (abstract-content.component.ts:51)
    at SafeSubscriber.__tryOrUnsub (Subscriber.js:185)
    at SafeSubscriber.next (Subscriber.js:124)
    at Subscriber._next (Subscriber.js:72)
    at Subscriber.next (Subscriber.js:49)
    at BehaviorSubject._subscribe (BehaviorSubject.js:14)
    at BehaviorSubject._trySubscribe (Observable.js:42)
    at BehaviorSubject._trySubscribe (Subject.js:81)
    at BehaviorSubject.subscribe (Observable.js:28)
    at Observable._subscribe (Observable.js:76)

The abstract-content-component mentioned in the error is actually a parent class with multiple implementations, being called by a child class which serves as a shell without much logic to allow for easy template switching.

The error itself seems puzzling (particularly line 51 referring to the if(sub) check), as how can a subscription not exist within its own event handler? This pattern has been successfully implemented elsewhere in my codebase, so why does this specific instance encounter issues?

Answer №1

To prevent this error from occurring, ensure that sub is declared as an attribute of the component.

sub: Subscription = new Subscription();
// ...

constructor(public contentService: ContractService) {
    let self = this;
    this.sub = contentService.loaded.subscribe(function(loaded) {
          // ...
          this.sub.unsubscribe();
          // ...
    });
}

However, it's recommended to avoid making these calls directly in the constructor. Consider placing them inside the ngOnInit method instead.


In order to ensure that the subscriber does not continue receiving values after the first one is returned by the service, unsubscribe once its task is completed (as you were doing), but make sure to declare sub as a component attribute and initialize it with new Subscription();


To prevent any lingering subscriptions, remember to unsubscribe from them in the component's ngOnDestroy method:

ngOnDestroy() {
  // ...
  this.sub.unsubscribe();
  // ...
}

If you want a deeper understanding of how the Observables pattern functions, refer to the Angular Docs on Observables

Answer №2

Prior to establishing the value for sub, you are double-checking its existence.

If you analyze your code methodically and step by step, you'll notice that you are initializing sub with a function, specifically in this instance, a subscription. This is acceptable on its own.

The problem arises from the fact that within the function itself, you are depending on the variable sub being already defined. It's not initialized yet because you are currently in the process of declaring it at that moment.

This results in the error message:

Cannot access 'sub' before initialization

It seems unclear what your intention is, since logically it appears as though you want to subscribe to something, but then immediately unsubscribe? However, performing an unsubscribe operation inside the subscribe block is not recommended.

If preferred, you can modify your code like so:

// Assuming you have a "loaded" variable declared above, based on the context where self is set to this
private unsubscribeStream = new Subject();

this.contentservice.loaded.pipe(
  filter(x => x !== null), // Filter out cases where x is non-existent
  takeUntil(this.unsubscribeStream)).subscribe(loaded => {
    this.loaded = loaded;
  });

// Upon component destruction, trigger the next value of unsubscribe stream to cancel any ongoing operations subscribed with takeUntil
ngOnDestroy() {
   this.unsubscribeStream.next();
}

Answer №3

Recently, I encountered a similar issue where I realized that my code was outdated and created when my knowledge of rxjs was limited. Looking back, I now consider it an anti-pattern to write code in that manner.

The ideal scenario is to receive only one emitted value and then complete the operation. Here are some options:

  1. My approach:

    contentService.loaded .pipe( first(), ) .subscribe(...);

  2. Alternatively, another effective method:

    contentService.loaded .pipe( take(1), ) .subscribe(...);

  3. Consider using Promises; they are still relevant. If you always require exactly ONE value, there may be no need for an Observable. (Just like how you wouldn't convert a number to a string if you only need a string because numbers have their own unique properties)

Stay vigilant!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What steps can I take to ensure my dynamic route functions correctly in NextJs?

// /home/[storeId]/layout.tsx import prismadb from "@/lib/prismadb"; import { auth } from "@clerk/nextjs/server"; import { redirect } from "next/navigation"; export default async function DashboardLayout({ children, params, ...

Enhance your coding experience with Angular Apollo Codegen providing intelligent suggestions for anonymous objects

Currently, I am exploring the integration of GraphQL with Angular. So far, I have been able to scaffold the schema successfully using the @graphql-codegen package. The services generated are functional in querying the database. However, I've noticed ...

"What is the best way to transform a POST curl request into an Angular HTTP POST request using gsutil for Google Cloud Storage

Currently utilizing GCP and GCS. I am attempting to send a POST request, however in the curl command below, I am unable to locate where to input the necessary data that needs to be sent. In this scenario, how can I implement a POST request using angular ...

Encountering a issue while running npm start with Angular 2 RC build

After upgrading from Angular2 beta 15 to the RC version, I encountered some errors while trying to run my application. typings/browser/ambient/es6-shim/index.d.ts(8,14): error TS2300: Duplicate identifier 'PropertyKey'. typings/browser/ambient/e ...

Following the recent update to IntelliJ IDEA 2022.1.3, the use of ::ng-deep has been marked

After updating the version of IntelliJ, I noticed that the ::ng-deep angular material selector is now marked as deprecated. Here's an example: <mat-form-field class="register-custom-select"> <mat-select formControlName="gende ...

Clearing the filename in a file type input field using React

When using this input field, only video files are accepted. If any other types of files are uploaded by enabling the "all files" option, an alert will be displayed. While this functionality is working correctly, a problem arises if a non-video file is adde ...

Eliminate the standard cell padding in nested HTML tables

In my setup, there is a parent table where each td tag in the tr tag contains a child table. Specifically, as shown in the example with Data WS-C3.... in the picture. [![<table class="table table--bordered table--nostripes table-top"> <thead& ...

NestJS TypeORM InjectRepository throwing an error: "Cannot access property 'prototype' of undefined"

Encountering an issue while trying to unit test. Here is the error message that I received: TypeError: Cannot read property 'prototype' of undefined export class UserService { constructor(@InjectRepository(User) private readonly userRepository ...

Execute environment validation on server during `next build` command

I have a src/config/server/ts file that contains the following code: 'use server'; import zod from 'zod'; if (typeof window !== 'undefined') { throw new Error('env should not be imported on the frontend!'); } co ...

The KeyConditionExpression is invalid due to the use of multiple attribute names within a single condition

I am trying to query a DynamoDB table using GraphQL TableName: "JobInfo", IndexName: "tableauGSI", KeyConditionExpression: "tableauGSI_Tableau = tableau AND #D BETWEEN :startDate AND :endDate", ExpressionAttributeNames: { "#D": "date" }, ...

What is the best practice for updating the state of a web component in a manner that can be tracked by history with vaadin-router?

I have a straightforward LitElement custom component that I would like to incorporate an "editing" state into. Although I already possess all the necessary information from the server, I am hesitant to introduce a new component solely for displaying an edi ...

Creating a button that displays the current day with Angular

I'm in the process of developing a timetable app that features buttons for the previous day, current day, and next day. How can I implement a button to specifically show the current day? HTML File <button type="button" (click)="previousDay()" ...

Troubleshooting issue with jest expect.any() failing to work with a custom class following migration from JavaScript to TypeScript

I recently made the switch to TypeScript in my project, and now some of my Jest tests are failing. It appears that the next function below is no longer being called with an AppError object, but with an Error object instead. Previously, the assertion expec ...

Trying out Angular2 service using a fabricated backend

Just a heads up: I know Angular2 is still in alpha and undergoing frequent changes. I am currently working with Angular2 and facing an issue with testing an injectable service that has a dependency on http. I want to test this service using a mock backend ...

Is there a way to customize Material UI theme types and make adjustments to existing types using Typescript?

One way to customize the material ui theme is by extending its type and adding new properties, as shown here: For example, if we want to add an appDrawer property, it can be done like this: declare module '@material-ui/core/styles/createMuiTheme&apos ...

What steps should I take to ensure the successful function of the App Routing system in this scenario?

After creating an Angular App, I encountered a challenge in one of my services. When I call the http.post method and subscribe to it, I aim to redirect to the previous page with a parameter (e.g., "http://localhost:3000/profile/aRandomName"). Unfortunately ...

Utilizing Typescript Generics in Arrow Function to Combine Two Arguments

For instance, I am working with this code in a .tsx file extension const Add = <T,>(arg0: T, arg1: T): T => arg0 + arg1; const A = Add(1, 2); const B = Add('1', '2') However, I am encountering an issue, as there is an error m ...

Explaining the union type using a list of data types

Is there a way to create a union type that strictly limits values to 'a', 'b', 'c' when using a list like const list: string[] = ['a', 'b', 'c']? I know one method is: const list = ['a' ...

Angular/Typescript code not functioning properly due to faulty expressions

What could be causing my {{ expression }} to malfunction? I have exhausted all options, yet the web browser fails to recognize this {{ expression }} or properly bind it using ng-bind. Instead, it either displays the {{ expression }} as is or not at all. C ...

Encountering an issue with Angular 2.0.1 Router where it throws an EmptyError due to

I am struggling to set up Angular 2 routing properly. I am currently using Angular 2 with Webpack. While looking at the Angular 2 webpack starter, I noticed they had webpack handling their html and links generation, but I was hoping to avoid webpacking my ...