Skip to content

Commit eae305b

Browse files
authored
Support @@trigger protocol in operator interval (#289)
* Add implementation and test * Add docs * Fix overloads error * Fix invalid type-tests (after adding overloads)
1 parent fcdc737 commit eae305b

File tree

4 files changed

+109
-23
lines changed

4 files changed

+109
-23
lines changed

src/interval/index.ts

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,20 @@ import {
99
is,
1010
} from 'effector';
1111

12+
export function interval<S extends unknown, F extends unknown>(config: {
13+
timeout: number | Store<number>;
14+
start: Event<S>;
15+
stop?: Event<F>;
16+
leading?: boolean;
17+
trailing?: boolean;
18+
}): { tick: Event<void>; isRunning: Store<boolean> };
19+
20+
export function interval(config: {
21+
timeout: number | Store<number>;
22+
leading?: boolean;
23+
trailing?: boolean;
24+
}): TriggerProtocol;
25+
1226
export function interval<S extends unknown, F extends unknown>({
1327
timeout,
1428
start,
@@ -17,11 +31,14 @@ export function interval<S extends unknown, F extends unknown>({
1731
trailing = false,
1832
}: {
1933
timeout: number | Store<number>;
20-
start: Event<S>;
34+
start?: Event<S>;
2135
stop?: Event<F>;
2236
leading?: boolean;
2337
trailing?: boolean;
24-
}): { tick: Event<void>; isRunning: Store<boolean> } {
38+
}): { tick: Event<void>; isRunning: Store<boolean> } & TriggerProtocol {
39+
const setup = (start ?? createEvent()) as Event<void>;
40+
const teardown = (stop ?? createEvent()) as Event<void>;
41+
2542
const tick = createEvent();
2643
const $isRunning = createStore(false);
2744
const $timeout = toStoreNumber(timeout);
@@ -65,19 +82,19 @@ export function interval<S extends unknown, F extends unknown>({
6582
});
6683

6784
guard({
68-
clock: start,
85+
clock: setup,
6986
source: $timeout,
7087
filter: $notRunning,
7188
target: timeoutFx,
7289
});
7390

7491
if (leading) {
75-
const onReady = guard({ clock: start, filter: $notRunning }) as Event<S>;
92+
const onReady = guard({ clock: setup, filter: $notRunning });
7693
sample({ clock: onReady, target: tick });
7794
}
7895

7996
sample({
80-
clock: start,
97+
clock: setup,
8198
fn: () => true,
8299
target: $isRunning,
83100
});
@@ -97,23 +114,29 @@ export function interval<S extends unknown, F extends unknown>({
97114
}),
98115
});
99116

100-
if (stop) {
101-
if (trailing) {
102-
sample({
103-
clock: stop,
104-
target: tick,
105-
});
106-
}
107-
108-
$isRunning.on(stop, () => false);
109-
117+
if (trailing) {
110118
sample({
111-
clock: stop,
112-
target: cleanupFx,
119+
clock: teardown,
120+
target: tick,
113121
});
114122
}
115123

116-
return { tick, isRunning: $isRunning };
124+
$isRunning.on(teardown, () => false);
125+
126+
sample({
127+
clock: teardown,
128+
target: cleanupFx,
129+
});
130+
131+
return {
132+
tick,
133+
isRunning: $isRunning,
134+
'@@trigger': {
135+
setup,
136+
teardown,
137+
fired: tick,
138+
},
139+
};
117140
}
118141

119142
function toStoreNumber(value: number | Store<number> | unknown): Store<number> {
@@ -126,3 +149,10 @@ function toStoreNumber(value: number | Store<number> | unknown): Store<number> {
126149
`timeout parameter in interval method should be number or Store. "${typeof value}" was passed`,
127150
);
128151
}
152+
153+
/**
154+
* @see {@link https://withease.pages.dev/protocols/trigger.html}
155+
*/
156+
type TriggerProtocol = {
157+
'@@trigger': { fired: Event<void>; setup: Event<void>; teardown: Event<void> };
158+
};

src/interval/interval.fork.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { allSettled, createEvent, fork, createStore, guard } from 'effector';
1+
import {
2+
allSettled,
3+
createEvent,
4+
fork,
5+
createStore,
6+
guard,
7+
createWatch,
8+
} from 'effector';
29
import { argumentHistory, wait, watch } from '../../test-library';
310
import { interval } from '.';
411

@@ -96,3 +103,30 @@ test('does not leaves unresolved timeout effect, if stopped', async () => {
96103

97104
expect(scope.getState($count)).toEqual(6);
98105
});
106+
107+
describe('@@trigger', () => {
108+
test('fire tick on start and stop after teardown', async () => {
109+
const listener = jest.fn();
110+
const intervalTrigger = interval({ timeout: 1 });
111+
112+
const scope = fork();
113+
114+
const unwatch = createWatch({
115+
unit: intervalTrigger['@@trigger'].fired,
116+
fn: listener,
117+
scope,
118+
});
119+
120+
allSettled(intervalTrigger['@@trigger'].setup, { scope });
121+
122+
await wait(1);
123+
expect(listener).toBeCalledTimes(1);
124+
125+
await allSettled(intervalTrigger['@@trigger'].teardown, { scope });
126+
127+
await wait(10);
128+
expect(listener).toBeCalledTimes(1);
129+
130+
unwatch();
131+
});
132+
});

src/interval/readme.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,27 @@ setTimeout(() => stopCounter(), 5000);
7878
```
7979

8080
[Try it](https://share.effector.dev/EOVzc3df)
81+
82+
### `@@trigger` protocol
83+
84+
`interval` supports [`@@trigger` protocol](https://withease.pages.dev/protocols/trigger.html). It means that you can use `interval` whatever you can use `trigger` with, just do not pass `start` and `stop` options.
85+
86+
```ts
87+
import { interval } from 'patronum';
88+
89+
somethingThatRequiresTrigger({
90+
trigger: interval({ timeout: 1000 }),
91+
});
92+
```
93+
94+
For example, you can use `interval` to refresh data in the Query from the library [Farfetched](https://farfetched.pages.dev/tutorial/trigger_api.html#external-triggers) using `@@trigger` protocol.
95+
96+
```ts
97+
import { keepFresh } from '@farfetched/core';
98+
import { interval } from 'patronum';
99+
100+
keepFresh(someQuery, {
101+
// 👇 Query will be refreshed each 1000 ms
102+
triggers: [interval({ timeout: 1000 })],
103+
});
104+
```

test-typings/interval.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,21 @@ import { interval } from '../src/interval';
3838
}),
3939
);
4040

41+
// @ts-expect-error
4142
interval({
4243
timeout: 100,
4344
start: createEvent<void>(),
4445
stop: createEvent<void>(),
45-
// @ts-expect-error
4646
leading: 1,
47-
// @ts-expect-error
4847
trailing: '',
4948
});
5049

50+
// @ts-expect-error
5151
interval({
5252
timeout: 100,
5353
start: createEvent<void>(),
5454
stop: createEvent<void>(),
55-
// @ts-expect-error
5655
leading: [],
57-
// @ts-expect-error
5856
trailing: null,
5957
});
6058
}

0 commit comments

Comments
 (0)