Is emitting a side effect event acceptable within an RxJS pipe?

Currently, I am utilizing RxJS within the context of an Angular application.

Within my service, there is functionality to reinitialize the entire application with different settings as needed.


@Injectable()
class BootstrapService{

 public initApplication(config: appConfig): Observable<boolean>{
        return someObservable;
 };
}

The intended use case scenario would be:


///.... code in part of application that allows user to reinit app

this.bootstrapService.initApplication({someconfig}).subscribe(result=>{
  if(result){
      //report success
   }else report failure
}
}

This functions similarly to a cold observable, resembling http calls.

Now, I have a requirement to send additional notifications to specific components (such as updating user menus, toolbars, etc. with new configuration from the server).

I plan to achieve this by emitting an event at the end of the pipe inside BoostrapService#initApplicaiton, like so:

 
public initApplication(config: appConfig): Observable<boolean>{
       return someObservable().pipe(tap(result=>if(result)this.subject.next(someEvent))
 };

This can be seen as a side effect, which is typically discouraged in functional programming, based on information available online.

So the question arises - is it acceptable to emit events as side effects, or should a different approach be taken?

Another potential solution that comes to mind involves creating a "hot" observable that isn't returned to the caller, but rather a shared stream is used instead:


appInitResult: Subject<boolean>
 public initApplication(config: appConfig): Observable<boolean>{
      someObservable().subscribe(r-> this.appInitResult.next(r));
  return this.appInitResult.asObservable();
 };

This way, everything can subscribe to the same stream, including the method caller.

Answer №1

Although it's best to avoid external side effects, there are times when they cannot be completely eliminated. It seems like the way you're handling this side effect is appropriate.

With that said, I'm curious about the specific scenario that necessitates reinitializing the app.

For example, in a situation where a user is logged in, you can establish a reactive data flow within your app. By placing the user observable at the forefront of this flow, any changes to the user object will trigger corresponding updates in all observables throughout the app.

While setting this up may seem complex, utilizing observables extensively is key to achieving seamless functionality.

Answer №2

It seems like you are utilizing Angular, so allow me to share my perspective on the situation.

If I understand correctly, your application involves multiple components that are interested in the same stream of events.

In such a scenario, I would recommend creating a myService with 2 APIs:

  • a public Observable that notifies the components about the event they are interested in
  • a public method that enables an external client of the myService to trigger the event notification

To implement this, the code would look something like this:

@Injectable()
class MyService{
 private _myEvent = new Subject<any>()
 public myEvent = this._myEvent.asObservable()

 public notifyEvent(event: any) {
   this._myEvent.next(event)
 };
}

Any component or service that wishes to utilize myService can access it through Dependency Injection and employ its functionalities.

For instance, a BootstrapService would be structured as follows:

@Injectable() class BootstrapService{ constructor(private myService: MyService) {}

public initApplication(config: appConfig) { // perform necessary actions to generate the event const _event = buildEvent(config) this.myService.notifyEvent(event) }; }

Another component requiring notifications can be designed similarly:

@Injectable()
class MyComponent{
 constructor(private myService: MyService) {}
 
 // use myService.myEvent as needed, such as subscribing to it in ngOnInit
 public ngOnInit() { 
        const _event = buildEvent(config)
        this.myService.myEvent.subscribe({
          next: event => {
             // perform actions using the event data
          }
        })
 };
}

Components structured in this manner can also directly utilize myService.myEvent within the template with the async pipe, especially if the objective is solely to display information present in the event.

You might find this article informative. While it discusses React, the concept of the service aligns with what was described above.

Lastly, in reference to your statement about "functional programming should avoid side effects," it should be interpreted cautiously.

Functional programming cannot completely eliminate side effects. A program requires side effects to perform useful actions like writing or displaying information. The key is to isolate these side effects, which is where functional programming can assist by isolating side effects.

In this case, the side effects are isolated within the subscribe logic.

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

"Exploring the process of creating a custom type by incorporating changes to an existing interface

One of the challenges I'm facing is defining an object based on a specific interface structure. The interface I have looks like this: interface Store { ReducerFoo : ReducerFooState; ReducerBar : ReducerBarState; ReducerTest : ReducerTestSt ...

Why is it that in Angular, console.log(11) is displayed before console.log(1)?

Can someone help me understand why my simple submit method is printing Console.log(11) before Console.log(1)? I'm confused about the order of execution. submit(value) { this.secServise.getUserById(this.currentUser.mgId).subscribe( uAddrs => { ...

Is there a way to eliminate properties in typescript without relying on the option feature?

I am struggling with removing properties in TypeScript. type Person<GN> = { getName: GN extends never ? never : GN, } const foo = <GN>(person: Person<GN>) => person const first = foo({}) // This should work const second = fo ...

How to retrieve a value only if it is truthy in JavaScript or TypeScript - Understanding the operator

In React components, a common scenario arises with code like this: <Carousel interval={modalOpen ? null : 8000}> It would be great if I could simplify it to something along these lines (although it's not valid): <Carousel interval={modalOpen ...

What are the steps to validate a form control in Angular 13?

My Angular 13 application has a reactive form set up as follows: I am trying to validate this form using the following approach: However, I encountered the following error messages: Additionally, it is important to note that within the component, there ...

The expected property 'label' is not found in the object type '{ handleClick: () => void; }', but it is required in the object type '{ handleClick: () => void; label: string; }'

I'm encountering difficulties when describing the types of my props. The issue arises with the following code: <PostButton handleClick={props.upvote}/> <PostButton2 handleClick={props.downvote}/> An error message is displayed: Pro ...

What are the potential drawbacks of utilizing HTML interpolation to modify CSS styles within Angular2+?

My objective was to modify the CSS styling based on whether the user was using a desktop or mobile device. To achieve this, I utilized an observable to determine if the window width exceeded 1000px. Main.ts Component import { Component, OnInit } from &ap ...

Event listener for scrolling in Angular Dart component

I am looking to implement a functionality where a "back to top" button appears once the user has scrolled the page by 500px. I have been trying to capture the scroll event in the main Div of my AppComponent. <div class="container" scrollable&g ...

What is the process for retrieving information from a retail outlet?

How can I retrieve data from the Vuex store using Vue.js? Below is my code: Vue.use(Vuex); export default new Vuex.Store({ modules: { data } }) data.js import axios from 'axios'; const state = { data: '' }; cons ...

Assessing asynchronous HTTP function with Jamine: Exceeding time limit - No response received within 5000ms

ISSUE: Dealing with asynchronous functions returning Promises that rely on nested HTTP calls. USED TECHNOLOGIES: Angular 9, Jasmine 3.4.0 ERROR ENCOUNTERED: Error: Timeout - Async callback was not invoked within 5000ms (set by jasmine.DEFAULT_TIMEOUT_INT ...

VSCode prioritizes importing files while disregarding any symbolic links in order to delve deeper into nested node

I'm encountering a problem with VSCode and TypeScript related to auto imports. Our application includes a service known as Manager, which relies on certain functions imported from a private npm package called Helpers. Both Manager and Helpers make us ...

Retrieving radio button value in Angular 2

Currently, I am attempting to retrieve the radio button value in an Angular 2 project. The radio buttons are defined in index.html as: <input type="radio" id="options1" name="function" value="create"> <input type="radio" id="options2" name="func ...

Retrieve the outermost shell of the VUEjs wrapper test-utils

While working on a VueJS test, I encountered an issue where accessing the outermost layer of HTML seemed impossible. No matter what methods I tried, the outermost layer was always ignored. Is there a way to gain access to this outermost layer so that I c ...

When evaluating code with eval, properties of undefined cannot be set, but the process works seamlessly without

Currently, I am attempting to utilize the eval() function to dynamically update a variable that must be accessed by path in the format myArray[0][0[1][0].... Strangely enough, when I try this approach, I encounter the following error: Uncaught TypeError: ...

The error message "Uncaught ReferenceError: exports is not defined and require" indicates that

I am currently developing an app using angularjs and typescript, but I've encountered a persistent error that has me stumped. Below is the snippet of my code: export var NgApp = new application.Startup(); ///<reference path="../../../../../typin ...

Defining RefObject effectively in TypeScript

Greetings everyone, I am a newcomer to TypeScript and currently attempting to create a type for a RefObject that is of type HTMLAudioElement. However, I have encountered an error message. The error states: Type 'MutableRefObject<HTMLAudioElement> ...

Determining the Type<> of a component based on a string in Angular 2

Can you retrieve the type of a component (Type<T>) based on a string value? For example: let typeStr: string = 'MyComponent'; let type: any = getTypeFromName(typeStr); // actual type ...

Performing Cypress testing involves comparing the token stored in the localStorage with the one saved in the clipboard

I am currently working on a button function that copies the token stored in localStorage to the clipboard. I am trying to write code that will compare the token in localStorage with the one in the clipboard in order to verify if the copy was successful. H ...

Styling Dynamic HTML Content in Angular: Tips and Tricks

After running this line of code, I have a generated element sub with a property of [sub]. <li [displayMode]="displayMode" template-menu-item style="cursor: pointer" [routerLink]="['/request/checkoutReview']" icon="fa-shopping-cart" name ...

I'm interested in utilizing Angular 2 to parse a CSV file and showcase the data in a table

Hello everyone, I am new to Angular 2/4 and I am trying to parse a CSV file and display the data in a table on a webpage. I have attempted some code, but the data only gets displayed in the console, not on the webpage itself. Can anyone help me out? hand ...