@solid-primitives/memo
Advanced tools
Comparing version 0.0.100 to 0.0.101
@@ -1,31 +0,60 @@ | ||
import { MemoOptions } from 'solid-js/types/reactive/signal'; | ||
import { Accessor } from 'solid-js'; | ||
import { MemoOptions, EffectOptions } from 'solid-js/types/reactive/signal'; | ||
import { Fn } from '@solid-primitives/utils'; | ||
declare type MemoOptionsWithValue<T> = MemoOptions<T> & { | ||
value?: T; | ||
}; | ||
declare type AsyncMemoCalculation<T, Init = undefined> = (prev: T | Init) => Promise<T> | T; | ||
/** | ||
* Lazily evaluated `createMemo`. Will run the calculation only if is being listened to. | ||
* Solid's `createReaction` that is based on pure computation *(runs before render, and is non-batching)* | ||
* | ||
* @param calc pure reactive calculation returning some value | ||
* @param options for configuring initial state: *(before first read)* | ||
* - `value` - initial value of the signal | ||
* - `init` - if **true**, will run the calculation initially, to get the initial value | ||
* @returns signal of a value that was returned by the calculation | ||
* @param onInvalidate callback that runs when the tracked sources trigger update | ||
* @param options set computation name for debugging pourposes | ||
* @returns track() function | ||
* | ||
* @see https://github.com/davedbase/solid-primitives/tree/main/packages/memo#createLazyMemo | ||
* @see https://github.com/davedbase/solid-primitives/tree/main/packages/memo#createPureReaction | ||
* | ||
* @example | ||
* const double = createLazyMemo(() => count() * 2) | ||
* const [count, setCount] = createSignal(0); | ||
* const track = createPureReaction(() => {...}); | ||
* track(count); | ||
* setCount(1); // triggers callback | ||
* | ||
* // sources need to be re-tracked every time | ||
* setCount(2); // doesn't trigger callback | ||
*/ | ||
declare function createLazyMemo<T>(calc: (prev: T) => T, options: MemoOptionsWithValueInit<T> & { | ||
declare function createPureReaction(onInvalidate: Fn, options?: EffectOptions): (tracking: Fn) => void; | ||
/** | ||
* Solid's `createMemo` which returned signal is debounced. | ||
* | ||
* @param calc reactive calculation returning signals value | ||
* @param timeoutMs The duration to debounce in ms | ||
* @param options specify initial value *(by default it will be undefined)* | ||
* | ||
* @see https://github.com/davedbase/solid-primitives/tree/main/packages/memo#createDebouncedMemo | ||
* | ||
* @example | ||
* const double = createDebouncedMemo(() => count() * 2, 200) | ||
*/ | ||
declare function createDebouncedMemo<T>(calc: (prev: T) => T, timeoutMs: number, options: MemoOptionsWithValue<T> & { | ||
value: T; | ||
init: true; | ||
}): Accessor<T>; | ||
declare function createLazyMemo<T>(calc: (prev: T) => T, options: MemoOptionsWithValueInit<T> & { | ||
declare function createDebouncedMemo<T>(calc: (prev: T | undefined) => T, timeoutMs: number, options?: MemoOptionsWithValue<T>): Accessor<T>; | ||
/** | ||
* Solid's `createMemo` which returned signal is throttled. | ||
* | ||
* @param calc reactive calculation returning signals value | ||
* @param timeoutMs The duration to throttle in ms | ||
* @param options specify initial value *(by default it will be undefined)* | ||
* | ||
* @see https://github.com/davedbase/solid-primitives/tree/main/packages/memo#createThrottledMemo | ||
* | ||
* @example | ||
* const double = createThrottledMemo(() => count() * 2, 200) | ||
*/ | ||
declare function createThrottledMemo<T>(calc: (prev: T) => T, timeoutMs: number, options: MemoOptionsWithValue<T> & { | ||
value: T; | ||
}): Accessor<T>; | ||
declare function createLazyMemo<T>(calc: (prev: T | undefined) => T, options: MemoOptionsWithValueInit<T> & { | ||
init: true; | ||
}): Accessor<T>; | ||
declare function createLazyMemo<T>(calc: (prev: T | undefined) => T, options?: MemoOptionsWithValueInit<T>): Accessor<T | undefined>; | ||
declare type AsyncMemoCalculation<T, Init = undefined> = (prev: T | Init) => Promise<T> | T; | ||
declare function createThrottledMemo<T>(calc: (prev: T | undefined) => T, timeoutMs: number, options?: MemoOptionsWithValue<T>): Accessor<T>; | ||
/** | ||
@@ -52,43 +81,50 @@ * Solid's `createMemo` that allows for asynchronous calculations. | ||
declare function createAsyncMemo<T>(calc: AsyncMemoCalculation<T>, options?: MemoOptionsWithValue<T>): Accessor<T | undefined>; | ||
/** | ||
* Solid's `createMemo` which returned signal is debounced. | ||
* Lazily evaluated `createMemo`. Will run the calculation only if is being listened to. | ||
* | ||
* @param calc reactive calculation returning signals value | ||
* @param timeoutMs The duration to debounce in ms | ||
* @param options specify initial value *(by default it will be undefined)* | ||
* @param calc pure reactive calculation returning some value | ||
* @param value the initial previous value *(in callback)* | ||
* @param options set computation name for debugging pourposes | ||
* @returns signal of a value that was returned by the calculation | ||
* | ||
* @see https://github.com/davedbase/solid-primitives/tree/main/packages/memo#createDebouncedMemo | ||
* @see https://github.com/davedbase/solid-primitives/tree/main/packages/memo#createLazyMemo | ||
* | ||
* @example | ||
* const double = createDebouncedMemo(() => count() * 2, 200) | ||
* const double = createLazyMemo(() => count() * 2) | ||
*/ | ||
declare function createDebouncedMemo<T>(calc: (prev: T) => T, timeoutMs: number, options: MemoOptionsWithValue<T> & { | ||
value: T; | ||
}): Accessor<T>; | ||
declare function createDebouncedMemo<T>(calc: (prev: T | undefined) => T, timeoutMs: number, options?: MemoOptionsWithValue<T>): Accessor<T>; | ||
declare function createLazyMemo<T>(calc: (prev: T) => T, value: T, options?: MemoOptions<T>): Accessor<T>; | ||
declare function createLazyMemo<T>(calc: (prev: T | undefined) => T, value?: undefined, options?: MemoOptions<T>): Accessor<T>; | ||
declare type CacheCalculation<Key, Value> = (key: Key, prev: Value | undefined) => Value; | ||
declare type CacheKeyAccessor<Key, Value> = (key: Key) => Value; | ||
declare type CacheOptions<Value> = MemoOptions<Value> & { | ||
size?: number; | ||
}; | ||
/** | ||
* Solid's `createMemo` which returned signal is throttled. | ||
* Custom, lazily-evaluated, cached memo. The caching is based on a `key`, it has to be declared up-front as a reactive source, or passed to the signal access function. | ||
* | ||
* @param calc reactive calculation returning signals value | ||
* @param timeoutMs The duration to throttle in ms | ||
* @param options specify initial value *(by default it will be undefined)* | ||
* @param key a reactive source, that will serve as cache key (later value access for the same key will be taken from cache instead of recalculated) | ||
* @param calc calculation function returning value to cache. the function is **tracking** - will recalculate when the accessed signals change. | ||
* @param options set maximum **size** of the cache, or memo options. | ||
* @returns signal access function | ||
* | ||
* @see https://github.com/davedbase/solid-primitives/tree/main/packages/memo#createThrottledMemo | ||
* @see https://github.com/davedbase/solid-primitives/tree/main/packages/memo#createCache | ||
* | ||
* @example | ||
* const double = createThrottledMemo(() => count() * 2, 200) | ||
* set the reactive key up-front | ||
* ```ts | ||
* const [count, setCount] = createSignal(1) | ||
* const double = createCache(count, n => n * 2) | ||
* // access value: | ||
* double() | ||
* ``` | ||
* or pass it to the access function (let's accessing different keys in different places) | ||
* ```ts | ||
* const double = createCache((n: number) => n * 2) | ||
* // access with key | ||
* double(count()) | ||
* ``` | ||
*/ | ||
declare function createThrottledMemo<T>(calc: (prev: T) => T, timeoutMs: number, options: MemoOptionsWithValue<T> & { | ||
value: T; | ||
}): Accessor<T>; | ||
declare function createThrottledMemo<T>(calc: (prev: T | undefined) => T, timeoutMs: number, options?: MemoOptionsWithValue<T>): Accessor<T>; | ||
declare function createCache<Key, Value>(key: Accessor<Key>, calc: CacheCalculation<Key, Value>, options?: CacheOptions<Value>): Accessor<Value>; | ||
declare function createCache<Key, Value>(calc: CacheCalculation<Key, Value>, options?: CacheOptions<Value>): CacheKeyAccessor<Key, Value>; | ||
declare type MemoOptionsWithValue<T> = MemoOptions<T> & { | ||
value?: T; | ||
}; | ||
declare type MemoOptionsWithValueInit<T> = MemoOptionsWithValue<T> & { | ||
init?: boolean; | ||
}; | ||
export { AsyncMemoCalculation, MemoOptionsWithValue, MemoOptionsWithValueInit, createAsyncMemo, createDebouncedMemo, createLazyMemo, createThrottledMemo }; | ||
export { AsyncMemoCalculation, CacheCalculation, CacheKeyAccessor, CacheOptions, MemoOptionsWithValue, createAsyncMemo, createCache, createDebouncedMemo, createLazyMemo, createPureReaction, createThrottledMemo }; |
@@ -1,43 +0,54 @@ | ||
// src/lazy.ts | ||
// src/index.ts | ||
import { | ||
createSignal, | ||
createComputed, | ||
createSignal, | ||
untrack, | ||
getOwner, | ||
onCleanup, | ||
createMemo, | ||
runWithOwner | ||
} from "solid-js"; | ||
function createLazyMemo(calc, options = {}) { | ||
var _a; | ||
const initValue = (_a = options.value) != null ? _a : void 0; | ||
const [state, setState] = createSignal(options.init ? calc(initValue) : initValue, options); | ||
let listeners = 0; | ||
let isActive = false; | ||
const owner = getOwner(); | ||
function recreateComputation() { | ||
if (listeners !== 1 || isActive) | ||
import debounce from "@solid-primitives/debounce"; | ||
import throttle from "@solid-primitives/throttle"; | ||
import { isFunction } from "@solid-primitives/utils"; | ||
function createPureReaction(onInvalidate, options) { | ||
const [trackedList, setTrackedList] = createSignal([]); | ||
let addedTracked = false; | ||
createComputed(() => { | ||
if (!trackedList().length) | ||
return; | ||
isActive = true; | ||
runWithOwner(owner, () => createComputed(() => { | ||
if (listeners > 0) | ||
setState((prev) => calc(prev)); | ||
else | ||
isActive = false; | ||
}, void 0, options)); | ||
} | ||
return () => { | ||
if (getOwner()) { | ||
listeners++; | ||
onCleanup(() => listeners--); | ||
recreateComputation(); | ||
if (addedTracked) { | ||
addedTracked = false; | ||
trackedList().forEach((tracking) => tracking()); | ||
} else { | ||
setTrackedList([]); | ||
untrack(onInvalidate); | ||
} | ||
return state(); | ||
}, options); | ||
return (tracking) => { | ||
addedTracked = true; | ||
setTrackedList((p) => [...p, tracking]); | ||
}; | ||
} | ||
// src/async.ts | ||
import { createComputed as createComputed2, createSignal as createSignal2, untrack } from "solid-js"; | ||
function createDebouncedMemo(calc, timeoutMs, options = {}) { | ||
const [state, setState] = createSignal(options.value, options); | ||
const [fn] = debounce(() => track(() => setState(calc)), timeoutMs); | ||
const track = createPureReaction(() => { | ||
fn(); | ||
track(() => calc(state())); | ||
}, options); | ||
track(() => setState(calc)); | ||
return state; | ||
} | ||
function createThrottledMemo(calc, timeoutMs, options = {}) { | ||
const [state, setState] = createSignal(options.value, options); | ||
const [fn] = throttle(() => track(() => setState(calc)), timeoutMs); | ||
const track = createPureReaction(fn, options); | ||
track(() => setState(calc)); | ||
return state; | ||
} | ||
function createAsyncMemo(calc, options = {}) { | ||
const [state, setState] = createSignal2(options.value, options); | ||
const [state, setState] = createSignal(options.value, options); | ||
const order = []; | ||
createComputed2(async () => { | ||
createComputed(async () => { | ||
const value = calc(untrack(state)); | ||
@@ -56,29 +67,45 @@ if (value instanceof Promise) { | ||
} | ||
// src/grouped.ts | ||
import { createReaction, createSignal as createSignal3 } from "solid-js"; | ||
import debounce from "@solid-primitives/debounce"; | ||
import throttle from "@solid-primitives/throttle"; | ||
function createDebouncedMemo(calc, timeoutMs, options = {}) { | ||
const [state, setState] = createSignal3(options.value, options); | ||
const [fn] = debounce(() => track(() => setState(calc)), timeoutMs); | ||
const track = createReaction(() => { | ||
fn(); | ||
track(() => calc(state())); | ||
}, options); | ||
track(() => setState(calc)); | ||
return state; | ||
function createLazyMemo(calc, value, options) { | ||
let memo; | ||
let listeners = 0; | ||
const owner = getOwner(); | ||
const recreateMemo = () => runWithOwner(owner, () => { | ||
memo = createMemo((prev) => { | ||
if (listeners) | ||
return calc(prev); | ||
memo = void 0; | ||
return prev; | ||
}, value, options); | ||
}); | ||
return () => { | ||
if (getOwner()) { | ||
listeners++; | ||
onCleanup(() => listeners--); | ||
} | ||
if (!memo) | ||
recreateMemo(); | ||
return memo(); | ||
}; | ||
} | ||
function createThrottledMemo(calc, timeoutMs, options = {}) { | ||
const [state, setState] = createSignal3(options.value, options); | ||
const [fn] = throttle(() => track(() => setState(calc)), timeoutMs); | ||
const track = createReaction(fn, options); | ||
track(() => setState(calc)); | ||
return state; | ||
function createCache(...args) { | ||
const cache = /* @__PURE__ */ new Map(); | ||
const owner = getOwner(); | ||
const key = isFunction(args[1]) ? args[0] : void 0, calc = isFunction(args[1]) ? args[1] : args[0], options = typeof args[1] === "object" ? args[1] : typeof args[2] === "object" ? args[2] : {}; | ||
const run = (key2) => { | ||
if (cache.has(key2)) | ||
return cache.get(key2)(); | ||
const memo = runWithOwner(owner, () => createLazyMemo((prev) => calc(key2, prev), void 0, options)); | ||
if (options.size === void 0 || cache.size < options.size) | ||
cache.set(key2, memo); | ||
return memo(); | ||
}; | ||
return key ? () => run(key()) : run; | ||
} | ||
export { | ||
createAsyncMemo, | ||
createCache, | ||
createDebouncedMemo, | ||
createLazyMemo, | ||
createPureReaction, | ||
createThrottledMemo | ||
}; |
{ | ||
"name": "@solid-primitives/memo", | ||
"version": "0.0.100", | ||
"version": "0.0.101", | ||
"description": "Collection of custom memo primitives. They extend Solid's createMemo functionality while keeping the usage similar.", | ||
@@ -19,3 +19,5 @@ "author": "Damian Tarnawski @thetarnav <gthetarnav@gmail.com>", | ||
"createDebouncedMemo", | ||
"createThrottledMemo" | ||
"createThrottledMemo", | ||
"createPureReaction", | ||
"createCache" | ||
], | ||
@@ -34,4 +36,4 @@ "category": "Reactivity" | ||
"scripts": { | ||
"start": "vite serve dev", | ||
"dev": "vite serve dev", | ||
"start": "vite serve dev --host", | ||
"dev": "yarn start", | ||
"build": "tsup", | ||
@@ -46,2 +48,3 @@ "test": "uvu -r solid-register" | ||
"dependencies": { | ||
"@solid-primitives/utils": "^0.2.0", | ||
"@solid-primitives/debounce": "^1.1.0", | ||
@@ -55,8 +58,8 @@ "@solid-primitives/throttle": "^1.1.0" | ||
"tslib": "^2.3.1", | ||
"typescript": "^4.5.3", | ||
"typescript": "^4.5.4", | ||
"tsup": "^5.11.1", | ||
"uvu": "^0.5.2", | ||
"unocss": "0.21.2", | ||
"vite": "2.7.10", | ||
"vite-plugin-solid": "2.2.1", | ||
"unocss": "0.24.3", | ||
"vite": "2.8.1", | ||
"vite-plugin-solid": "2.2.5", | ||
"solid-app-router": "^0.2.0", | ||
@@ -68,2 +71,2 @@ "@solid-primitives/mouse": "^1.0.2" | ||
} | ||
} | ||
} |
114
README.md
@@ -14,2 +14,4 @@ # @solid-primitives/memo | ||
- [`createThrottledMemo`](#createThrottledMemo) - Memo which returned signal is throttled. | ||
- [`createPureReaction`](#createPureReaction) - A `createReaction` that runs before render _(non-batching)_. | ||
- [`createCache`](#createCache) - Custom, lazily-evaluated, memo, with caching based on keys. | ||
@@ -37,15 +39,11 @@ ## Installation | ||
const double = createLazyMemo(() => count() * 2); | ||
double(); // T: number | undefined | ||
double(); // T: number | ||
``` | ||
Because it executes lazily, the calculation won't run if nothing is listening to it, that also includes the initial run by default. It causes the signal to might return `undefined` when accessed for the first time. | ||
Set the initial value, or type of the previous value in calculation function will be `T | undefined`. | ||
```ts | ||
// use the options to enable initial run | ||
const double = createLazyMemo(() => count() * 2, { init: true }); | ||
double(); // T: number | ||
// or set the initial value | ||
const double = createLazyMemo(() => count() * 2, { value: 0 }); | ||
double(); // T: number | ||
// set the initial value | ||
const memo = createLazyMemo(prev => count() + prev, 123); | ||
memo(); // T: number | ||
``` | ||
@@ -144,2 +142,100 @@ | ||
## `createPureReaction` | ||
Solid's [`createReaction`](#https://www.solidjs.com/docs/latest/api#createreaction) that is based on pure computation _(runs before render, and is non-batching)_ | ||
### How to use it | ||
It's usage exactly matches the original. The only difference is in when the callback is being executed, the normal createReaction runs it after render, similar to how effects work, while the createPureReaction is more like createComputed. | ||
```ts | ||
import { createPureReaction } from "@solid-primitives/memo" | ||
const [count, setCount] = createSignal(0); | ||
const track = createPureReaction(() => {...}); | ||
track(count); | ||
setCount(1); // triggers callback | ||
// sources need to be re-tracked every time | ||
setCount(2); // doesn't trigger callback | ||
``` | ||
### Definition | ||
```ts | ||
function createPureReaction(onInvalidate: Fn, options?: EffectOptions): (tracking: Fn) => void; | ||
``` | ||
## `createCache` | ||
Custom, lazily-evaluated, cached memo. The caching is based on a `key`, it has to be declared up-front as a reactive source, or passed to the signal access function. | ||
### how to use it | ||
It takes params: | ||
- `key` a reactive source, that will serve as cache key (later value access for the same key will be taken from cache instead of recalculated) | ||
- `calc` calculation function returning value to cache. the function is **tracking** - will recalculate when the accessed signals change. | ||
- `options` set maximum **size** of the cache, or memo options. | ||
Returns a signal access function. | ||
#### Import | ||
```ts | ||
import { createCache } from "@solid-primitives/memo"; | ||
``` | ||
#### Setting the key up-front as a reactive source | ||
```ts | ||
const [count, setCount] = createSignal(1); | ||
const double = createCache(count, n => n * 2); | ||
// access value: | ||
double(); | ||
``` | ||
#### Provide the key by passing it to the access function | ||
let's accessing different keys in different places | ||
```ts | ||
const [count, setCount] = createSignal(1); | ||
const double = createCache((n: number) => n * 2); | ||
// access value with key: | ||
double(count()); | ||
``` | ||
#### Calculation function is reactive | ||
will recalculate when the accessed signals change. | ||
```ts | ||
// changing number creates new entry in cache | ||
const [number, setNumber] = createSignal(1); | ||
// changing divisor will force cache to be recalculated | ||
const [divisor, setDivisor] = createSignal(1); | ||
// calculation subscribes to divisor signal | ||
const result = createCache(number, n / divisor()); | ||
``` | ||
### Definition | ||
```ts | ||
function createCache<Key, Value>( | ||
key: Accessor<Key>, | ||
calc: CacheCalculation<Key, Value>, | ||
options?: CacheOptions<Value> | ||
): Accessor<Value>; | ||
function createCache<Key, Value>( | ||
calc: CacheCalculation<Key, Value>, | ||
options?: CacheOptions<Value> | ||
): CacheKeyAccessor<Key, Value>; | ||
type CacheCalculation<Key, Value> = (key: Key, prev: Value | undefined) => Value; | ||
type CacheKeyAccessor<Key, Value> = (key: Key) => Value; | ||
type CacheOptions<Value> = MemoOptions<Value> & { size?: number }; | ||
``` | ||
## Changelog | ||
@@ -146,0 +242,0 @@ |
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
25515
377
248
4
1
+ Added@solid-primitives/utils@0.2.2(transitive)