-
Notifications
You must be signed in to change notification settings - Fork 88
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add effectOnceIf helper function (#419)
* feat: add effectOnceIf helper function * docs: effectOnceIf --------- Co-authored-by: Chau Tran <[email protected]>
- Loading branch information
1 parent
70611b8
commit bd56a88
Showing
9 changed files
with
180 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"name": "Lorenzo D'Ianni", | ||
"twitter": "https://x.com/lorenzodianni", | ||
"linkedin": "https://www.linkedin.com/in/lorenzo-d-ianni-3990bba6/", | ||
"github": "https://github.com/lorenzodianni" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
--- | ||
title: effectOnceIf | ||
description: ngxtension/effect-once-if | ||
entryPoint: effect-once-if | ||
badge: stable | ||
contributors: ['lorenzo-dianni'] | ||
--- | ||
|
||
`effectOnceIf` is a helper function that allows you to create an effect that will be executed only once if a certain condition occurs. | ||
|
||
## Usage | ||
|
||
```ts | ||
@Component({}) | ||
class Example { | ||
count = signal(0); | ||
|
||
effectOnceIfRef = effectOnceIf( | ||
// condition function: if it returns a truly value, the execution function will run | ||
() => this.count() > 3, | ||
// execution function: will run only once | ||
(valueReturnedFromCondition, onCleanup) => { | ||
console.log( | ||
`triggered with value returned: ${valueReturnedFromCondition}`, | ||
); | ||
onCleanup(() => console.log('cleanup')); | ||
}, | ||
); | ||
} | ||
|
||
// example.count.set(1); | ||
// -> nothing happens | ||
// example.count.set(4); | ||
// -> log: triggered with value returned: true | ||
// -> log: cleanup | ||
// example.count.set(6); | ||
// -> nothing happens | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# ngxtension/effect-once-if | ||
|
||
Secondary entry point of `ngxtension`. It can be used by importing from `ngxtension/effect-once-if`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"lib": { | ||
"entryFile": "src/index.ts" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{ | ||
"name": "ngxtension/effect-once-if", | ||
"$schema": "../../../node_modules/nx/schemas/project-schema.json", | ||
"projectType": "library", | ||
"sourceRoot": "libs/ngxtension/effect-once-if/src", | ||
"targets": { | ||
"test": { | ||
"executor": "@nx/jest:jest", | ||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"], | ||
"options": { | ||
"jestConfig": "libs/ngxtension/jest.config.ts", | ||
"testPathPattern": ["effect-once-if"], | ||
"passWithNoTests": true | ||
}, | ||
"configurations": { | ||
"ci": { | ||
"ci": true, | ||
"codeCoverage": true | ||
} | ||
} | ||
}, | ||
"lint": { | ||
"executor": "@nx/eslint:lint", | ||
"outputs": ["{options.outputFile}"] | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { Component, signal } from '@angular/core'; | ||
import { TestBed } from '@angular/core/testing'; | ||
import { effectOnceIf } from './effect-once-if'; | ||
|
||
function createTestComponent(triggerValue: number) { | ||
const log: string[] = []; | ||
const logCleanup: string[] = []; | ||
|
||
@Component({ standalone: true, template: '' }) | ||
class Example { | ||
count = signal(0); | ||
|
||
ref = effectOnceIf( | ||
() => this.count() === triggerValue, | ||
(value, onCleanup) => { | ||
log.push(`received ${triggerValue}: ${value}`); | ||
onCleanup(() => { | ||
logCleanup.push(`cleaning effect with condition ${triggerValue}`); | ||
}); | ||
}, | ||
); | ||
} | ||
|
||
return { component: Example, log, logCleanup }; | ||
} | ||
|
||
describe(effectOnceIf.name, () => { | ||
it('should run effect once and cleanup', () => { | ||
const test = createTestComponent(2); | ||
const fixture = TestBed.createComponent(test.component); | ||
fixture.detectChanges(); | ||
expect(test.log).toEqual([]); | ||
expect(test.logCleanup).toEqual([]); | ||
|
||
fixture.componentInstance.count.set(1); | ||
fixture.detectChanges(); | ||
expect(test.log).toEqual([]); | ||
expect(test.logCleanup).toEqual([]); | ||
|
||
fixture.componentInstance.count.set(2); | ||
fixture.detectChanges(); | ||
expect(test.log).toEqual(['received 2: true']); | ||
expect(test.logCleanup).toEqual(['cleaning effect with condition 2']); | ||
|
||
fixture.componentInstance.count.set(3); | ||
fixture.detectChanges(); | ||
expect(test.log).toEqual(['received 2: true']); | ||
expect(test.logCleanup).toEqual(['cleaning effect with condition 2']); | ||
}); | ||
|
||
it('should run effect once and cleanup on init', () => { | ||
const test = createTestComponent(0); | ||
const fixture = TestBed.createComponent(test.component); | ||
fixture.detectChanges(); | ||
expect(test.log).toEqual(['received 0: true']); | ||
expect(test.logCleanup).toEqual(['cleaning effect with condition 0']); | ||
|
||
fixture.componentInstance.count.set(1); | ||
fixture.detectChanges(); | ||
expect(test.log).toEqual(['received 0: true']); | ||
expect(test.logCleanup).toEqual(['cleaning effect with condition 0']); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { | ||
CreateEffectOptions, | ||
effect, | ||
EffectCleanupRegisterFn, | ||
EffectRef, | ||
runInInjectionContext, | ||
untracked, | ||
} from '@angular/core'; | ||
import { assertInjector } from 'ngxtension/assert-injector'; | ||
|
||
export function effectOnceIf<T = any>( | ||
condition: () => T, | ||
execution: ( | ||
valueFromCondition: NonNullable<T>, | ||
onCleanup: EffectCleanupRegisterFn, | ||
) => void, | ||
options?: Omit<CreateEffectOptions, 'allowSignalWrites' | 'manualCleanup'>, | ||
): EffectRef { | ||
const assertedInjector = assertInjector(effectOnceIf, options?.injector); | ||
return runInInjectionContext(assertedInjector, () => { | ||
const effectRef = effect((onCleanup) => { | ||
const hasCondition = condition(); | ||
if (hasCondition) { | ||
untracked(() => execution(hasCondition, onCleanup)); | ||
effectRef.destroy(); | ||
} | ||
}, options); | ||
return effectRef; | ||
}); | ||
} | ||
|
||
export type EffectOnceIfConditionFn<T> = Parameters<typeof effectOnceIf<T>>[0]; | ||
export type EffectOnceIfExecutionFn<T> = Parameters<typeof effectOnceIf<T>>[1]; | ||
export type EffectOnceIfOptions<T> = Parameters<typeof effectOnceIf<T>>[2]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './effect-once-if'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters