Skip to content

Commit

Permalink
Enh: flushDelayed options
Browse files Browse the repository at this point in the history
  • Loading branch information
Vovan-VE committed May 23, 2023
1 parent 4024ecb commit 015624f
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 6 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

### 0.5.0 (2023-05-23)

- Add: `flushDelayed()` options:
- `flushDelay` now can be `Store<number>`;
- `filter` - optional `Store<boolean>` to conditionally disable flushes.
- Doc: `flushDelayed()` was not documented in README.

## 0.4.0 (2022-12-13)

- **BREAKING**: `withPersistentMap()` now requires the given Driver to be
Expand Down
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ npm i @cubux/effector-persistent
## API

See also [`@cubux/storage-driver`](https://github.com/cubux-net/ts-storage-driver).
It supports `localStorage`/`sessionStorage` and `indexedDB`.

### `withPersistent()`

Expand Down Expand Up @@ -139,3 +140,38 @@ interface WithPersistentOptions<
| `wakeUp` | <code>Store&lt;State&gt; &#124; ((state: State) =&gt; void)</code> | `undefined` | Alternative target which will receive initial state read from driver on initialization. When `undefined`, the source Store will be used. |
| `serialize` | <code>(input: Value) =&gt; Serialized &#124; Promise&lt;Serialized&gt;</code> | `undefined` | Serialization before writing data to driver. |
| `unserialize` | <code>(output: Serialized) =&gt; Value &#124; Promise&lt;Value&gt;</code> | `undefined` | Unserialization after reading data from driver. |

## Helper API

### `flushDelayed()`

Setup delayed flushes from `source` unit to `target` unit with whe given debounce
interval.

Prefer [`patronum` `debounce()`](https://patronum.effector.dev/methods/debounce/)
instead.

```ts
function flushDelayed<T>(options: Options<T>): (() => void);
```

It takes options described below and returns a function to stop watching and
interrupt planned flush.

| Options | Type | Default | Description |
|--------------|---------------------------------------------------|--------------|---------------------------------------------------------------------------|
| `source` | <code>Store&lt;T&gt; &#124; Event&lt;T&gt;</code> | **Required** | Source unit to watch |
| `target` | `(payload: T) => void` | **Required** | Receiver to flush data to |
| `flushDelay` | <code>number &#124; Store&lt;number&gt;</code> | `1000` | Debounce timeout to await before flush |
| `filter` | <code>Store&lt;boolean&gt;</code> | `undefined` | When specified, flushes will work only when this filter `Store` is `true` |

Actual flush `target` will be called only after `source` unit will stop
triggering for at least duration in `flushDelay`. That is while `source` is
keeping triggering continuous within this duration, flush `target` will never
be called.

When `flushDelay` is `Store<number>`, it's value will take effect only on next
`source` update.

When `filter` is used, it will abort current planned flush, when its value
becomes `false`.
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
{
"name": "@cubux/effector-persistent",
"version": "0.4.0",
"version": "0.5.0",
"description": "Persist data in effector store.",
"keywords": [
"effector",
"store",
"persistent"
"persist",
"persistent",
"localStorage",
"sessionStorage",
"indexedDB"
],
"author": {
"name": "Vovan-VE",
Expand Down
28 changes: 24 additions & 4 deletions src/flushDelayed.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Event, Store } from 'effector';
import { Event, guard, Store } from 'effector';

interface Options<T> {
/**
Expand All @@ -14,26 +14,46 @@ interface Options<T> {
* called only after `source` unit will stop triggering for at least this
* duration. That is while `source` is keeping triggering continuous within
* this duration, flush `target` will never be called.
*
* When `Store<number>` is used, it's value will take effect only on next
* `source` update.
*/
flushDelay?: number;
flushDelay?: number | Store<number>;
/**
* When specified, flushes will work only when this filter `Store` is `true`.
* When its value becomes `false`, it will abort current planned flush.
*/
filter?: Store<boolean>;
}

/**
* Setup delayed flushes from `source` unit to `target` with whe given debounce
* interval. Default interval duration is `1000` (1 sec).
* @return Function to stop watching and interrupt pending flush.
* @return Function to stop watching and interrupt planned flush.
*/
export function flushDelayed<T>({
source,
target,
flushDelay = 1000,
filter,
}: Options<T>) {
let tId: ReturnType<typeof setTimeout>;

const sub = source.watch((state: T) => {
clearTimeout(tId);
tId = setTimeout(() => target(state), flushDelay);
if (!filter || filter.getState()) {
tId = setTimeout(
() => target(state),
typeof flushDelay === 'number' ? flushDelay : flushDelay.getState()
);
}
});
if (filter) {
guard({
source: filter.updates,
filter: (b) => !b,
}).watch(() => clearTimeout(tId));
}

return () => {
clearTimeout(tId);
Expand Down

0 comments on commit 015624f

Please sign in to comment.