Skip to content

Commit

Permalink
feat(cdk): add tuiMemo for decorator less way
Browse files Browse the repository at this point in the history
  • Loading branch information
splincode committed Oct 13, 2024
1 parent 9c91f0f commit 069811a
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 0 deletions.
1 change: 1 addition & 0 deletions projects/cdk/utils/miscellaneous/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export * from './is-present';
export * from './is-string';
export * from './is-valid-url';
export * from './mark-control-as-touched-and-validate';
export * from './memo';
export * from './nullable-same';
export * from './provide';
export * from './provide-options';
Expand Down
30 changes: 30 additions & 0 deletions projects/cdk/utils/miscellaneous/memo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
interface Options {
storedOnce: boolean;
}

export function tuiMemo<T extends unknown[], Result>(
fn: (...args: T) => Result,
options: Partial<Options> = {
storedOnce: true,
},
) {
const cache = new Map<string, Result>();

return function (...args: T): Result {
const key = args.map((arg) => `${typeof arg}:${arg}`).join('|');

if (cache.has(key)) {
return cache.get(key) as Result;
}

const result = fn(...args);

if (options.storedOnce && cache.size > 0) {
cache.clear();
}

cache.set(key, result);

return result;
};
}
111 changes: 111 additions & 0 deletions projects/cdk/utils/miscellaneous/test/memo.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import {tuiMemo} from '@taiga-ui/cdk';

describe('tuiMemo', () => {
it('calls getter only once and then sets result as a value property on the object', () => {
class TestClass {
private readonly memoized = tuiMemo(() => {
this.count++;

return 'test';
});

public count = 0;

public get someGetter(): string {
return this.memoized();
}
}

const testObject = new TestClass();

expect(testObject.count).toBe(0);
expect(testObject.someGetter).toBe('test');
expect(testObject.count).toBe(1);
expect(testObject.someGetter).toBe('test');
expect(testObject.someGetter).toBe('test');
expect(testObject.count).toBe(1);
});

it('memorizes method result', () => {
const name = 'Alex';
const age = 30;

class TestClass {
public readonly combine = tuiMemo(this.memoized);
private memoized(name: string, age: number): [string, number] {
return [name, age];
}
}

const testObject = new TestClass();
const result = testObject.combine(name, age);

expect(testObject.combine(name, age)).toBe(result);
expect(testObject.combine(name, age)).toBe(result);
});

it('has access to this', () => {
const name = 'Alex';
const age = 30;

class TestClass {
public readonly combine = tuiMemo(this.memoized.bind(this));

constructor(public readonly prefix: string) {}

public memoized(name: string, age: number): [string, string, number] {
return [this.prefix, name, age];
}
}

const testObject = new TestClass('awesome');
const result = testObject.combine(name, age);

expect(testObject.combine(name, age)).toBe(result);
});

it('memoize function without arguments', () => {
class TestClass {
public sideEffect = 0;
public readonly getRange = tuiMemo(this.memoized.bind(this));

public get range(): number[] {
return this.getRange();
}

public memoized(): number[] {
this.sideEffect++;

return Array.from({length: 5}, (_item, index: number) => index + 1);
}
}

const testObject = new TestClass();

expect(testObject.range).toEqual([1, 2, 3, 4, 5]);
expect(testObject.range).toEqual([1, 2, 3, 4, 5]);
expect(testObject.range).toEqual([1, 2, 3, 4, 5]);
expect(testObject.sideEffect).toBe(1);
});

it('memoize function without arguments and return undefined by default', () => {
class TestClass {
public sideEffect = 0;
public readonly voidFn = tuiMemo(this.memoized.bind(this));

public memoized(): void {
this.sideEffect++;

// necessary for the test
return undefined;
}
}

const testObject = new TestClass();

expect(testObject.voidFn()).toBeUndefined();
expect(testObject.voidFn()).toBeUndefined();
expect(testObject.voidFn()).toBeUndefined();
expect(testObject.sideEffect).toBe(1);
});
});

0 comments on commit 069811a

Please sign in to comment.