SubjectBehavior: changing the value persistently between subscriptions

Let's consider a scenario where we have a BehaviorSubject containing a User interface:

interface User {
  firstName: string;
  lastName: string;
}

let user: User = {
  firstName: 'Cuong',
  lastName: 'Le',
};

let bs = new BehaviorSubject<User>(user);

There are two subscriptions in play here. sub1 attempts to modify the first name, while sub2 subscribes later and sees the updated first name from the changes made by sub1:

let sub1 = bs.subscribe((u) => {
  u.firstName = 'An';

  console.log(u);
});

let sub2 = bs.subscribe((u) => {
  console.log(u);
});

In larger Angular applications, debugging becomes a challenge when such scenarios arise. How can we ensure immutability of values when subscribing?

I'm interested in exploring deeply immutable solutions to prevent any external code from altering the data unintentionally.

Answer №1

One way to indicate the type on the BehaviorSubject as Readonly<User> instead of User is by using the utility type Readonly<T>:

let bs = new BehaviorSubject<Readonly<User>>(user);

This will prompt TypeScript to display a warning message when attempting to modify:

https://i.sstatic.net/Rq1NOm.png

Cannot assign to 'firstName' because it is a read-only property. (2540)

Answer №2

I have encountered this issue multiple times and have found a solution that works when dealing with simple non-circular objects.

The key requirement is to use the lodash library, which provides a fast method for deep copying without referencing original objects. From my research, I have yet to come across a faster alternative.

let bs = (new BehaviorSubject<User>(user)).pipe(    
     map((userData) => _.cloneDeep(userData)) 
);

I have applied this technique to large state objects and have consistently been impressed by its efficiency in creating copies. By using this approach, any data emitted from the subject remains isolated from the original source, preventing changes from affecting the original data.

Answer №3

By incorporating the guidance provided in this solution and encapsulating your type within the Immutable utility type as shown, you can effectively address your issue. In contrast to the traditional readonly keyword or ReadOnly utility type, the Immutable interface illustrated here ensures that all properties are recursively marked as read-only.

interface Person {
  firstName: string;
  lastName: string;
  address: { 
    street: string;
  } 
}

let person: Person = {
  firstName: 'Alice',
  lastName: 'Smith',
  address: { 
    street: '456 Elm St.'
  }
};

let obs = new BehaviorSubject<Immutable<Person>>(person);

let sub = obs.subscribe((p) => {
  p.address.street = '789'; //<-- Cannot assign to 'street' because it is a read-only property.
  console.log(p);
});

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

Utilizing jQuery or Javascript to obtain the title of the current webpage, find a specific string within it, and then apply a class to the corresponding element

Let's imagine I start with this: <title>Banana</title> and also have some navigation set up like this: <ul id="navigation"> <li> <a href=#">Banana</a> </li> </ul> Upon loading the sit ...

The CSRF token is mysteriously altering in places it should remain unchanged

Implementing the code below to prevent CSRF vulnerability if (!isset($_POST['_token'])) { $_SESSION['_token'] = bin2hex(random_bytes(20)); } I then use the token in hidden inputs named _token. In my main.js file, I include the foll ...

Form not functioning properly due to issues with formGroup

Struggling with an issue in my login form implementation. Uncaught Error: Template parse errors: Can't bind to 'formGroup' since it isn't a known property of 'form'. ("<form [ERROR ->][formGroup]="form" (ngSubmit)="onSu ...

Using a Vue.js component in a Laravel Blade template can be achieved by first registering the component

I added a component registration in my app.js as shown below: Vue.component('navbar', require('./components/Navbar.vue')); Now I am looking to import that component into my blade.php file like so: <template> <nav class="navb ...

What steps should I take to address the issue with my navbar?

I've written some code that creates a hamburger icon on mobile devices. When the user clicks it, a wave effect covers the screen, but I'm facing an issue where the wave doesn't cover the input field. My query is: how can I make the input fi ...

Error: JSON parsing error encountered for token < at position 0 within the context of Google Sheets Apps Script Tutorial

I'm currently working through a Google Sheets Apps Scripts editor tutorial and I've reached module 4. Unfortunately, I've encountered an issue with the code I copied directly from the module. The error message I'm seeing is: SyntaxError ...

Is it possible to execute a function upon exiting my Node application?

Currently, I'm developing a project for my school using Node.js on a Raspberry Pi. The script I have created will be running for extended periods of time to control LEDs. Is there a method that allows me to execute a function upon exiting the program ...

Error: Element type is invalid: a string was anticipated, but not found

I recently experimented with the example provided in React's documentation at this link and it functioned perfectly. My objective now is to create a button using material-ui. Can you identify what is causing the issue in this code? import * as R ...

Using Vue: How to utilize v-slot variables in JavaScript

Can the values of the v-slot of a component be accessed in the script? For instance, consider the following template: <cron-core v-model="value" :periods="periods" :format="format" v-slot="{fields, period, error}"> {{period}} <div v-for="fiel ...

Scroll function not functioning properly in Internet Explorer

When attempting to use scroll(x,y) in Internet Explorer 10 with JavaScript, I encountered an issue when trying to run the script on a website. Is there an alternative method that works for IE? This is part of a Java Selenium test where I need to scroll wit ...

Error message displayed when attempting to make a jQuery ajax call to an https URL: "Access

I am facing difficulty in setting up a client script to make an AJAX call to a web service on a different domain, particularly because the web service is using HTTPS. The web service is MVC4 WebApi (REST) and the client script is also running from an HTTPS ...

Updating documents in MongoDB periodically at specified intervals

I have developed a unique "lowest bid auction" system where users can set a base price, a price drop amount, and a price drop interval. For instance, if a user sets the base price as $1000, price drop amount as $100, and price drop interval as 1 hour, the ...

Use the column name instead of the index with Jquery's eq() function

Looking for a way to use a static column name in jQuery to get the 5th column text in a table and change the text of a textbox $('textbox').val($(e.target).closest("tr").find('td:eq(4)').text()); If the index of the columns is changed ...

"Converting a Service Worker from JavaScript to TypeScript: A Step-by-Step

Scenario I am in the process of converting my service-worker file from JavaScript to TypeScript in order to improve maintenance. Below is a snippet from my sw.js file: /* eslint-disable no-console */ self.addEventListener("push", (event) => { ... }); ...

Shifting an html element from side to side using javascript click event

I am new to utilizing JavaScript in conjunction with HTML elements, and I am seeking assistance in crafting a function for such interaction. The goal is to have an image that will shift either left or right based on its previous movement. For instance, up ...

How can I access data entries in Firebase that have a particular key?

I've got some data stored in firebase with a structure like this: "application": { "companies": { "firebase": { "creation": { "name": "Firebase Inc", "location": "USA" }, "google": { "creattion": { ...

Include the data-title attribute within the <td> elements of my datatables

To apply the magic of jQuery datatables to all my tables, I use datatables. For responsive tables, I add data-title to my td's. Is there a way to automatically add data-title to all my td's like below? <td data-title="Fruit">Apple</td&g ...

Refreshing and reloading within the same jQuery function

My PHP application involves the use of 2 PHP files. chart.php - This page includes a Google chart. <div id="chart_div" style="height:100%;width:100%"> </div> <script type="text/javascript"> google.charts.load('current', ...

What is the best approach to creating unit tests for pipes in Angular?

Can you provide guidance on writing a unit test case for Angular pipes? import { Pipe, PipeTransform } from '@angular/core'; import { IDropDownItem } from '@app/interfaces'; import { IDepartment } from '../interfaces'; class ...

Is there a way to turn off the "defer" feature in an Angular build?

After compiling my Angular project, I noticed that the compiler automatically adds the "defer" attribute to the script tag in my "index.html" file. However, I need to disable this feature. Is there a way to do it? I am currently working with Angular versi ...