@solid-primitives/rootless
Advanced tools
Comparing version 1.3.2 to 1.4.0
@@ -1,2 +0,2 @@ | ||
import { Owner } from 'solid-js'; | ||
import { Owner, Accessor } from 'solid-js'; | ||
import { AnyFunction } from '@solid-primitives/utils'; | ||
@@ -56,3 +56,3 @@ | ||
* @returns function, registering reactive owner as one of the listeners, returns the value {@link factory} returned. | ||
* @see https://github.com/davedbase/solid-primitives/tree/main/packages/rootless#createSingletonRoot | ||
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createSingletonRoot | ||
* @example | ||
@@ -89,3 +89,58 @@ * const useState = createSingletonRoot(() => { | ||
declare function createHydratableSingletonRoot<T>(factory: (dispose: VoidFunction) => T): () => T; | ||
/** | ||
* Options for {@link createRootPool}. | ||
*/ | ||
type RootPoolOptions = { | ||
/** | ||
* Size of the root pool. Defaults to `100`. | ||
*/ | ||
limit?: number; | ||
}; | ||
/** | ||
* Callback function for {@link createRootPool}. Called when a new root is created. | ||
* @param arg An accessor that returns the argument passed to {@link RootPoolFunction}. | ||
* @param active An accessor that returns the active state of the root. | ||
* When `false`, root is not being used and is waiting in the pool to be reused. | ||
* @param dispose A function that disposes the root and prevents it from being reused. | ||
* @returns The result of {@link RootPoolFunction}. | ||
*/ | ||
type RootPoolFactory<TArg, TResult> = (arg: Accessor<TArg>, active: Accessor<boolean>, dispose: VoidFunction) => TResult; | ||
/** | ||
* A function returned by {@link createRootPool}. | ||
* @param arg The argument passed to {@link RootPoolFactory}. | ||
*/ | ||
type RootPoolFunction<TArg, TResult> = (..._: void extends TArg ? [arg?: TArg] : [arg: TArg]) => TResult; | ||
/** | ||
* Creates a pool of roots, that can be reused. Useful for creating components that are mounted and unmounted frequently. | ||
* When the root is created, it will call the {@link factory} function with a {@link RootPoolFactory} callback. | ||
* Roots are created by calling the returned function, after cleanup they won't be disposed but instead put back into the pool to be reused. | ||
* Next time the function is called, it will reuse the root from the pool and update it with the new {@link arg}. | ||
* | ||
* @param factory A function that will be called when a new root is created. See {@link RootPoolFactory}. | ||
* @param options Options for the root pool. See {@link RootPoolOptions}. | ||
* @returns A function that creates and reuses roots. See {@link RootPoolFunction}. | ||
* | ||
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/rootless#createRootPool | ||
* | ||
* @example | ||
* ```tsx | ||
* const useCounter = createRootPool((arg, active, dispose) => { | ||
* const [count, setCount] = createSignal(arg()) | ||
* | ||
* createEffect(() => { | ||
* if (!active()) return | ||
* // so some side effect | ||
* console.log("count", count()) | ||
* }) | ||
* | ||
* return <button onClick={() => setCount(count() + 1)}>Count: {count()}</button> | ||
* }) | ||
* | ||
* return <Show when={frequentlyChangedCondidion()}> | ||
* {useCounter(1)} | ||
* </Show> | ||
* ``` | ||
*/ | ||
declare function createRootPool<TArg, TResult>(factory: RootPoolFactory<TArg, TResult>, options?: RootPoolOptions): RootPoolFunction<TArg, TResult>; | ||
export { createBranch, createCallback, createDisposable, createHydratableSingletonRoot, createSharedRoot, createSingletonRoot, createSubRoot }; | ||
export { RootPoolFactory, RootPoolFunction, RootPoolOptions, createBranch, createCallback, createDisposable, createHydratableSingletonRoot, createRootPool, createSharedRoot, createSingletonRoot, createSubRoot }; |
@@ -1,4 +0,4 @@ | ||
import { getOwner, createRoot, runWithOwner, onCleanup, sharedConfig } from 'solid-js'; | ||
import { getOwner, createRoot, runWithOwner, onCleanup, sharedConfig, createSignal, batch } from 'solid-js'; | ||
import { isServer } from 'solid-js/web'; | ||
import { asArray, access } from '@solid-primitives/utils'; | ||
import { asArray, access, trueFn, noop, createMicrotask } from '@solid-primitives/utils'; | ||
@@ -49,3 +49,69 @@ // src/index.ts | ||
} | ||
function createRootPool(factory, options = {}) { | ||
if (isServer) { | ||
const owner2 = getOwner(); | ||
return (args) => createRoot((dispose) => factory(() => args, trueFn, dispose), owner2); | ||
} | ||
let length = 0; | ||
const { limit = 100 } = options, pool = new Array(limit), owner = getOwner(), mapRoot = factory.length > 1 ? (dispose, [args, set]) => { | ||
const [active, setA] = createSignal(true); | ||
const root = { | ||
dispose, | ||
set, | ||
setA, | ||
active, | ||
v: factory(args, active, () => disposeRoot(root)) | ||
}; | ||
return root; | ||
} : (dispose, [args, set]) => ({ | ||
dispose, | ||
set, | ||
setA: trueFn, | ||
active: trueFn, | ||
v: factory(args, trueFn, noop) | ||
}), limitPool = createMicrotask(() => { | ||
if (length > limit) { | ||
for (let i = limit; i < length; i++) { | ||
pool[i].dispose(); | ||
pool[i] = void 0; | ||
} | ||
length = limit; | ||
} | ||
}), cleanupRoot = (root) => { | ||
if (root.dispose !== noop) { | ||
pool[length++] = root; | ||
root.setA(false); | ||
limitPool(); | ||
} | ||
}, disposeRoot = (root) => { | ||
root.dispose(); | ||
root.dispose = noop; | ||
if (root.active()) | ||
root.setA(false); | ||
else { | ||
pool[pool.indexOf(root)] = pool[--length]; | ||
pool[length] = void 0; | ||
} | ||
}; | ||
onCleanup(() => { | ||
for (let i = 0; i < length; i++) | ||
pool[i].dispose(); | ||
length = 0; | ||
}); | ||
return (arg) => { | ||
let root; | ||
if (length) { | ||
root = pool[--length]; | ||
pool[length] = void 0; | ||
batch(() => { | ||
root.set(() => arg); | ||
root.setA(true); | ||
}); | ||
} else | ||
root = createRoot((dispose) => mapRoot(dispose, createSignal(arg)), owner); | ||
onCleanup(() => cleanupRoot(root)); | ||
return root.v; | ||
}; | ||
} | ||
export { createBranch, createCallback, createDisposable, createHydratableSingletonRoot, createSharedRoot, createSingletonRoot, createSubRoot }; | ||
export { createBranch, createCallback, createDisposable, createHydratableSingletonRoot, createRootPool, createSharedRoot, createSingletonRoot, createSubRoot }; |
{ | ||
"name": "@solid-primitives/rootless", | ||
"version": "1.3.2", | ||
"version": "1.4.0", | ||
"description": "A collection of helpers that aim to simplify using reactive primitives outside of reactive roots, and managing disposal of reactive roots.", | ||
@@ -19,3 +19,4 @@ "author": "Damian Tarnawski @thetarnav <gthetarnav@gmail.com>", | ||
"createDisposable", | ||
"createSharedRoot" | ||
"createSharedRoot", | ||
"createRootPool" | ||
], | ||
@@ -43,8 +44,10 @@ "category": "Reactivity" | ||
"keywords": [ | ||
"solid", | ||
"primitives", | ||
"reactivity", | ||
"root", | ||
"solid", | ||
"primitives" | ||
"ownership" | ||
], | ||
"dependencies": { | ||
"@solid-primitives/utils": "^6.0.0" | ||
"@solid-primitives/utils": "^6.1.1" | ||
}, | ||
@@ -55,2 +58,4 @@ "peerDependencies": { | ||
"scripts": { | ||
"dev": "vite serve dev", | ||
"page": "vite build dev", | ||
"build": "jiti ../../scripts/build.ts", | ||
@@ -57,0 +62,0 @@ "test": "vitest -c ../../configs/vitest.config.ts", |
@@ -18,2 +18,3 @@ <p> | ||
- [`createSingletonRoot`](#createSingletonRoot) - Share "global primitives" across multiple reactive scopes. | ||
- [`createRootPool`](#createRootPool) - Creates a pool of reactive roots, that can be reused. | ||
@@ -171,4 +172,53 @@ ## Installation | ||
## `createRootPool` | ||
Creates a pool of roots, that can be reused. Useful for creating components that are mounted and unmounted frequently. | ||
When the root is created, it will call the factory function. | ||
Roots are created by calling the returned function, after cleanup they won't be disposed but instead put back into the pool to be reused. | ||
Next time the function is called, it will reuse the root from the pool and update it with the new data. | ||
### How to use it | ||
`createRootPool` primitive takes two arguments: | ||
- `factory` - A function that will be called when a new root is created. | ||
- `options` - Options for the root pool. | ||
- `limit` - Pool limit, defaults to `100`. Roots that are not used will be disposed when the limit is reached. | ||
Returns a function that creates and reuses roots. | ||
```tsx | ||
import { createRootPool } from "@solid-primitives/rootless"; | ||
const useCounter = createRootPool((arg, active, dispose) => { | ||
const [count, setCount] = createSignal(arg()); | ||
createEffect(() => { | ||
if (!active()) return; | ||
// so some side effect | ||
console.log("count", count()); | ||
}); | ||
return <button onClick={() => setCount(count() + 1)}>Count: {count()}</button>; | ||
}); | ||
return <Show when={frequentlyChangedCondidion()}>{useCounter(1)}</Show>; | ||
``` | ||
### Usage with `<For>` | ||
`createRootPool` can be combined with `<For>` (or any other control-flow component) to reuse already created HTML Elements while getting the same benefits of stable connection between rendered elements and the reference to the source item. | ||
```tsx | ||
const pool = createRootPool(item => <MyListItem item={item()}>) | ||
return <For each={items()}>{item => pool(item)}</For> | ||
``` | ||
> **Warning** | ||
> Using `createRootPool` with `<For>` creates an un-keyed control-flow — meaning that a single element can be reused by more than one item (not at the same time). Which can cause styles, animations, and other side effects to leak between items. | ||
> It's meant to be used only as a performance optimization, and only when you're sure that the side effects won't leak between items. | ||
## Changelog | ||
See [CHANGELOG.md](./CHANGELOG.md) |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
24596
375
223