Yevhen Klymentiev
dark
light
console
darkness
y.klymentiev@gmail.com
Reusable Snippets|Practical utility code for everyday use — custom-built and ready to share

oncePerKey

Creates a function that ensures the callback is only called once per unique key. Includes a .reset() method to clear the internal call tracking.

TypeScript
Copied!
1/**
2 * Creates a function that ensures the callback is only called once per unique key.
3 * Includes a .reset() method to clear the internal call tracking.
4 *
5 * @param fn - The function to call.
6 * @returns A function that takes a key and calls `fn` only once per unique key.
7 */
8export function oncePerKey<K = string>(
9  fn: (key: K) => void
10): ((key: K) => void) & { reset: () => void } {
11  const called = new Set<K>();
12
13  const wrapper = (key: K) => {
14    if (!called.has(key)) {
15      called.add(key);
16      fn(key);
17    }
18  };
19
20  wrapper.reset = () => {
21    called.clear();
22  };
23
24  return wrapper;
25}
  • Per-Key Execution Control

    Ensures that the callback is only invoked once for each unique key, providing granular, key-specific memoization.

  • Efficient Set-Based Tracking

    Uses a Set to efficiently store and check previously seen keys with O(1) performance.

  • Resettable Behavior

    Includes a .reset() method to clear the internal state, allowing re-use of the same instance in new cycles or contexts.

  • Flexible Key Typing

    Supports custom key types via a generic type parameter (K), improving type safety and adaptability.

Tests | Examples

TypeScript
Copied!
1test('oncePerKey - calls function only once per key', () => {
2  const spy = jest.fn();
3  const once = oncePerKey(spy);
4
5  once('a');
6  once('a');
7  once('b');
8  once('b');
9
10  expect(spy).toHaveBeenCalledTimes(2);
11  expect(spy).toHaveBeenCalledWith('a');
12  expect(spy).toHaveBeenCalledWith('b');
13});
14
15test('oncePerKey - reset() allows keys to be reused', () => {
16  const spy = jest.fn();
17  const once = oncePerKey(spy);
18
19  once('x');
20  once('x');
21
22  once.reset();
23
24  once('x');
25  expect(spy).toHaveBeenCalledTimes(2);
26});
27
28test('oncePerKey - works with numbers as keys', () => {
29  const spy = jest.fn();
30  const once = oncePerKey<number>(spy);
31
32  once(1);
33  once(1);
34  once(2);
35  expect(spy).toHaveBeenCalledTimes(2);
36});

Common Use Cases

  • Logging or Analytics Deduplication

    Ensure certain events (e.g., per user, per session, per item) are tracked only once.

  • Notification or Alert Control

    Show warnings or messages only once per context (e.g., per field, per resource, per route).

  • One-Time Setup per Resource

    Initialize or load something once per unique identifier (e.g., per tab, component, or entity).

  • Testing and Mocking

    Limit the execution of mock callbacks to avoid multiple invocations per key during test runs.

  • Memoization-like Control

    Create side-effect behavior that runs only once per parameter (e.g., feature flag activation or event registration).

Codebase: Utilities -> Functions -> oncePerKey | Yevhen Klymentiev