Sitemap
The Frontend Fuel

The Frontend Fuel

Exploring JavaScript, web development, and modern frameworks. Sharing insights, best practices, and real world coding challenges. Let's code smarter 🚀

RxJS distinctUntilChanged

3 min readApr 6, 2025

--

Press enter or click to view image in full size

distinctUntilChanged is used to prevent emitting duplicate consecutive values in a stream.

✅ Example Use Case

Let’s assume you have an input box and want to perform a search only when the input value changes (distinct), and not on every key stroke with the same value.

💡 Step-by-Step Implementation

  1. Angular Component HTML
<input type="text" [formControl]="searchControl" placeholder="Search here..." />

2. Angular Component TypeScript

import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { debounceTime, distinctUntilChanged } from 'rxjs/operators';

@Component({
selector: 'app-search',
templateUrl: './search.component.html'
})
export class SearchComponent implements OnInit {
searchControl = new FormControl('');

ngOnInit(): void {
this.searchControl.valueChanges
.pipe(
debounceTime(300), // wait for 300ms pause in events
distinctUntilChanged() // only emit when value is different from previous
)
.subscribe(value => {
console.log('Search term:', value);
// You can call a search API here
});
}
}

🧠 What’s Happening Here?

✅ valueChanges: Emits every time the input value changes.

✅ debounceTime(300): Waits for 300ms after the user stops typing.

✅ distinctUntilChanged(): Prevents emitting the same value consecutively.

🍔 Analogy: Fast-Food Order Display

Imagine a screen in a fast-food kitchen that shows the latest order to the chefs.

Without distinctUntilChanged

Every time a customer sends any order — even if it’s the same as the previous one, it shows up on the screen

Order: Burger 🍔 → Displayed
Order: Burger 🍔 (again) → Displayed again (unnecessarily)

With distinctUntilChanged

Get The Frontend Fuel’s stories in your inbox

Join Medium for free to get updates from this writer.

The system checks: “Is this order the same as the last one?” If yes, skip it!

Order: Burger 🍔 → Displayed
Order: Burger 🍔 → Ignored (already cooking it!)
Order: Fries 🍟 → Displayed
Order: Fries 🍟 → Ignored

This way, only new or changed inputs trigger actions — saving effort, time, and processing.

Let’s build a complete example using

⚙️ FormControl for capturing user input

⚙️ debounceTime to wait for user to stop typing

⚙️ distinctUntilChanged to ignore duplicate searches

⚙️ filter to ignore short/empty values

⚙️ switchMap to cancel previous requests and call an API only for the latest input

✅ Scenario: Real-Time Search (e.g., search users by name)

<input type="text" [formControl]="searchControl" placeholder="Search user..." />
<ul>
<li *ngFor="let user of users">{{ user.name }}</li>
</ul>

⚙️ 2. Component TS

import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { debounceTime, distinctUntilChanged, filter, switchMap } from 'rxjs/operators';
import { of } from 'rxjs';
import { UserService } from './user.service';

@Component({
selector: 'app-user-search',
templateUrl: './user-search.component.html'
})
export class UserSearchComponent implements OnInit {
searchControl = new FormControl('');
users: any[] = [];

constructor(private userService: UserService) {}

ngOnInit(): void {
this.searchControl.valueChanges.pipe(
debounceTime(300),
filter(value => value && value.length > 2), // only if 3+ characters
distinctUntilChanged(),
switchMap(query => this.userService.searchUsers(query)) // cancels old request
).subscribe(result => {
this.users = result;
});
}
}

🔌 3. User Service (Simulated API Call)

import { Injectable } from '@angular/core';
import { of } from 'rxjs';
import { delay } from 'rxjs/operators';

@Injectable({ providedIn: 'root' })
export class UserService {
private dummyUsers = [
{ name: 'Alice' },
{ name: 'Bob' },
{ name: 'Charlie' },
{ name: 'David' }
];

searchUsers(query: string) {
const filtered = this.dummyUsers.filter(user =>
user.name.toLowerCase().includes(query.toLowerCase())
);
return of(filtered).pipe(delay(500)); // simulate network delay
}
}

🚀 Flow Summary:

⚙️ User types → waits 300ms

⚙️ If input is 3+ characters and has changed → API call is made

⚙️ If the user types again quickly → previous request is canceled (thanks to switchMap)

--

--

Written by The Frontend Fuel

Exploring JavaScript, web development, and modern frameworks. Sharing insights, best practices, and real world coding challenges. Let's code smarter 🚀