@owanturist/signal-react 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +29 -0
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +62 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/package.json +8 -8
package/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021, Anton Ovechkin
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";function t(t,n){return null!=t?t:n()}Object.defineProperty(exports,"__esModule",{value:!0});var n=require("react");function o(t,n){return Object.is(t,n)}var e=require("@owanturist/signal/monitor-factory"),r=require("use-sync-external-store/with-selector");function u(o,u){const[c,i]=function(o){const e=n.useRef.call(void 0,null);return t(e.current,()=>e.current={_:o()}),e.current._}(()=>{const t=new(0,e.MonitorFactory);return[()=>t.create,n=>t.connect(n)]}),s=n.useCallback.call(void 0,t=>o(t()),[o]);return r.useSyncExternalStoreWithSelector.call(void 0,i,c,c,s,u)}function c(t){return t}exports.useComputed=function(e,r,c){const i=n.useCallback.call(void 0,t=>"function"==typeof e?e(t):e.read(t),t(r,()=>[e])),s=function(t){const o=n.useRef.call(void 0,{_:t,t:(...t)=>o.current._(...t)});return o.current._=t,o.current.t}((n,e)=>{const r=t(function(t){let n,o=t[0],e=1;for(;e<t.length;){const r=t[e],u=t[e+1];if(e+=2,("optionalAccess"===r||"optionalCall"===r)&&null==o)return;"access"===r||"optionalAccess"===r?(n=o,o=u(o)):"call"!==r&&"optionalCall"!==r||(o=u((...t)=>o.call(n,...t)),n=void 0)}return o}([c,"optionalAccess",t=>t.equals]),()=>o);return r(n,e)}),l=u(i,s);return n.useDebugValue.call(void 0,l),l},exports.useMonitor=function(){return u(c)};//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/signal/signal/packages/signal-react/dist/index.cjs","../src/use-computed.ts","../../tools/src/is-function.ts","../../tools/src/is-strict-equal.ts","../src/_internal/use-create-monitor.ts","../src/_internal/use-permanent.ts","../src/_internal/use-handler.ts","../../tools/src/identity.ts","../src/use-monitor.ts"],"names":["useRef","useCallback"],"mappings":"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","file":"/home/runner/work/signal/signal/packages/signal-react/dist/index.cjs","sourcesContent":[null,"import type { Equal, Monitor, ReadableSignal, Signal } from \"@owanturist/signal\"\nimport { useCallback, useDebugValue } from \"react\"\n\nimport { isFunction } from \"~/tools/is-function\"\nimport { isStrictEqual } from \"~/tools/is-strict-equal\"\n\nimport type { DependencyList } from \"./dependency-list\"\nimport { useCreateMonitor } from \"./_internal/use-create-monitor\"\nimport { useHandler } from \"./_internal/use-handler\"\n\n/**\n * The options for the {@link useComputed} hook.\n *\n * @template TValue the type of the resulting value.\n *\n * @version 1.0.0\n */\ninterface UseComputedOptions<TValue> {\n /**\n * The equality check function determines whether or not the factory result is different.\n * If the factory result is different, a host component re-renders.\n * In some cases specifying the function leads to better performance because it prevents unnecessary updates.\n *\n * @default Object.is\n */\n readonly equals?: null | Equal<TValue>\n}\n\n/**\n * A hook reads an {@link signal} value whenever it writes but enqueues a re-render only when the resulting value is different from the previous.\n *\n * @template TValue the type of the {@link Signal} value.\n *\n * @param signal anything that implements the {@link ReadableSignal} interface.\n *\n * @returns the {@link signal}'s value.\n *\n * @version 1.0.0\n */\nfunction useComputed<TValue>(signal: ReadableSignal<TValue>): TValue\n\n/**\n * A hook that executes the {@link compute} function whenever any of the involved {@link Signal}s' values update but enqueues a re-render only when the resulting value is different from the previous.\n *\n * @template TResult the type of the {@link compute} result.\n *\n * @param compute a function that provides {@link Monitor} as the first argument and subscribes to all {@link Signal}s calling the {@link Signal.read} method inside the function.\n * @param dependencies optional array of dependencies of the {@link compute} function. If not defined, the {@link compute} function is called on every render.\n * @param options optional {@link UseComputedOptions}.\n * @param options.equals the {@link Equal} function that determines whether or not the {@link compute} result is different from the previous one. Defaults to {@link Object.is}.\n *\n * @returns the {@link compute} function result.\n *\n * @version 1.0.0\n */\nfunction useComputed<TResult>(\n compute: (monitor: Monitor) => TResult,\n dependencies?: DependencyList,\n options?: UseComputedOptions<TResult>,\n): TResult\n\nfunction useComputed<TResult>(\n computeOrReadableSignal: ((monitor: Monitor) => TResult) | ReadableSignal<TResult>,\n dependencies?: DependencyList,\n options?: UseComputedOptions<TResult>,\n): TResult {\n const transform = useCallback(\n (monitor: Monitor) => {\n if (isFunction(computeOrReadableSignal)) {\n return computeOrReadableSignal(monitor)\n }\n\n return computeOrReadableSignal.read(monitor)\n },\n // biome-ignore lint/correctness/useExhaustiveDependencies: pass dependencies as is or factory is the only dependency\n dependencies ?? [computeOrReadableSignal],\n )\n\n const equals = useHandler((prev: TResult, next: TResult) => {\n const fn = options?.equals ?? isStrictEqual\n\n return fn(prev, next)\n })\n\n const value = useCreateMonitor(transform, equals)\n\n useDebugValue(value)\n\n return value\n}\n\nexport type { UseComputedOptions }\nexport { useComputed }\n","type DefinitelyFunction<T> =\n // biome-ignore lint/complexity/noBannedTypes: use Function to ensure correct typing\n Extract<T, Function> extends never ? Function : Extract<T, Function>\n\nfunction isFunction<T>(\n // biome-ignore lint/complexity/noBannedTypes: use Function to ensure correct typing\n data: Function | T,\n): data is DefinitelyFunction<T> {\n return typeof data === \"function\"\n}\n\nexport { isFunction }\n","function isStrictEqual<T>(left: T, right: T): boolean {\n return Object.is(left, right)\n}\n\nexport { isStrictEqual }\n","import type { Monitor } from \"@owanturist/signal\"\nimport { MonitorFactory } from \"@owanturist/signal/monitor-factory\"\nimport { useCallback } from \"react\"\nimport { useSyncExternalStoreWithSelector } from \"use-sync-external-store/with-selector\"\n\nimport { usePermanent } from \"./use-permanent\"\n\nfunction useCreateMonitor<T>(\n transform: (monitor: Monitor) => T,\n equals?: (left: T, right: T) => boolean,\n): T {\n const [selectCreateMonitor, connect] = usePermanent(() => {\n const factory = new MonitorFactory()\n\n return [\n //\n () => factory.create,\n (emit: VoidFunction) => factory.connect(emit),\n ]\n })\n\n const select = useCallback((create: () => Monitor) => transform(create()), [transform])\n\n return useSyncExternalStoreWithSelector(\n connect,\n selectCreateMonitor,\n selectCreateMonitor,\n select,\n equals,\n )\n}\n\nexport { useCreateMonitor }\n","import { useRef } from \"react\"\n\nfunction usePermanent<TValue>(init: () => TValue): TValue {\n /**\n * According to the official React documentation\n * it is ok to write to the ref object during initialization.\n *\n * > Normally, writing or reading ref.current during render is not allowed.\n * > However, it’s fine in this case because the result is always the same,\n * > and the condition only executes during initialization so it’s fully predictable.\n *\n * @link https://react.dev/reference/react/useRef#avoiding-recreating-the-ref-contents\n */\n const ref = useRef<null | { _value: TValue }>(null)\n\n ref.current ??= { _value: init() }\n\n return ref.current._value\n}\n\nexport { usePermanent }\n","import { useRef } from \"react\"\n\nfunction useHandler<TArgs extends ReadonlyArray<unknown>, TResult>(\n handler: (...args: TArgs) => TResult,\n): (...args: TArgs) => TResult {\n /**\n * Despise React official documentation pointing out:\n *\n * > Do not write or read ref.current during rendering, except for initialization.\n * > This makes your component’s behavior unpredictable.\n *\n * While technically useLatest does write to ref.current during rendering (after the initial one), it's a widely accepted pattern because:\n * - It doesn't trigger re-renders.\n * - The side effect is predictable and serves a specific purpose (always having the latest value).\n * - It helps solve the common \"stale closure\" problem.\n * This pattern is recommended and used in many React resources despite the general warning in the useRef documentation.\n * The key is that the update to the ref doesn't directly influence the rendering output of the component in the same render cycle.\n *\n * @link https://react.dev/reference/react/useRef#caveats\n * @link https://react.dev/reference/react/useRef#avoiding-recreating-the-ref-contents\n */\n\n const ref = useRef({\n _value: handler,\n _getter: (...args: TArgs) => ref.current._value(...args),\n })\n\n ref.current._value = handler\n\n return ref.current._getter\n}\n\nexport { useHandler }\n","function identity<T>(value: T): T {\n return value\n}\n\nexport { identity }\n","import type { Monitor } from \"@owanturist/signal\"\n\nimport { identity } from \"~/tools/identity\"\n\nimport { useCreateMonitor } from \"./_internal/use-create-monitor\"\n\n/**\n * A hook that returns a {@link Monitor}.\n *\n * @returns A {@link Monitor} instance.\n *\n * @version 1.0.0\n */\nfunction useMonitor(): Monitor {\n return useCreateMonitor(identity)\n}\n\nexport { useMonitor }\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Equal, ReadableSignal, Monitor } from '@owanturist/signal';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
type DependencyList = ReadonlyArray<unknown>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The options for the {@link useComputed} hook.
|
|
10
|
+
*
|
|
11
|
+
* @template TValue the type of the resulting value.
|
|
12
|
+
*
|
|
13
|
+
* @version 1.0.0
|
|
14
|
+
*/
|
|
15
|
+
interface UseComputedOptions<TValue> {
|
|
16
|
+
/**
|
|
17
|
+
* The equality check function determines whether or not the factory result is different.
|
|
18
|
+
* If the factory result is different, a host component re-renders.
|
|
19
|
+
* In some cases specifying the function leads to better performance because it prevents unnecessary updates.
|
|
20
|
+
*
|
|
21
|
+
* @default Object.is
|
|
22
|
+
*/
|
|
23
|
+
readonly equals?: null | Equal<TValue>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A hook reads an {@link signal} value whenever it writes but enqueues a re-render only when the resulting value is different from the previous.
|
|
27
|
+
*
|
|
28
|
+
* @template TValue the type of the {@link Signal} value.
|
|
29
|
+
*
|
|
30
|
+
* @param signal anything that implements the {@link ReadableSignal} interface.
|
|
31
|
+
*
|
|
32
|
+
* @returns the {@link signal}'s value.
|
|
33
|
+
*
|
|
34
|
+
* @version 1.0.0
|
|
35
|
+
*/
|
|
36
|
+
declare function useComputed<TValue>(signal: ReadableSignal<TValue>): TValue;
|
|
37
|
+
/**
|
|
38
|
+
* A hook that executes the {@link compute} function whenever any of the involved {@link Signal}s' values update but enqueues a re-render only when the resulting value is different from the previous.
|
|
39
|
+
*
|
|
40
|
+
* @template TResult the type of the {@link compute} result.
|
|
41
|
+
*
|
|
42
|
+
* @param compute a function that provides {@link Monitor} as the first argument and subscribes to all {@link Signal}s calling the {@link Signal.read} method inside the function.
|
|
43
|
+
* @param dependencies optional array of dependencies of the {@link compute} function. If not defined, the {@link compute} function is called on every render.
|
|
44
|
+
* @param options optional {@link UseComputedOptions}.
|
|
45
|
+
* @param options.equals the {@link Equal} function that determines whether or not the {@link compute} result is different from the previous one. Defaults to {@link Object.is}.
|
|
46
|
+
*
|
|
47
|
+
* @returns the {@link compute} function result.
|
|
48
|
+
*
|
|
49
|
+
* @version 1.0.0
|
|
50
|
+
*/
|
|
51
|
+
declare function useComputed<TResult>(compute: (monitor: Monitor) => TResult, dependencies?: DependencyList, options?: UseComputedOptions<TResult>): TResult;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A hook that returns a {@link Monitor}.
|
|
55
|
+
*
|
|
56
|
+
* @returns A {@link Monitor} instance.
|
|
57
|
+
*
|
|
58
|
+
* @version 1.0.0
|
|
59
|
+
*/
|
|
60
|
+
declare function useMonitor(): Monitor;
|
|
61
|
+
|
|
62
|
+
export { type DependencyList, type UseComputedOptions, useComputed, useMonitor };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Equal, ReadableSignal, Monitor } from '@owanturist/signal';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
type DependencyList = ReadonlyArray<unknown>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The options for the {@link useComputed} hook.
|
|
10
|
+
*
|
|
11
|
+
* @template TValue the type of the resulting value.
|
|
12
|
+
*
|
|
13
|
+
* @version 1.0.0
|
|
14
|
+
*/
|
|
15
|
+
interface UseComputedOptions<TValue> {
|
|
16
|
+
/**
|
|
17
|
+
* The equality check function determines whether or not the factory result is different.
|
|
18
|
+
* If the factory result is different, a host component re-renders.
|
|
19
|
+
* In some cases specifying the function leads to better performance because it prevents unnecessary updates.
|
|
20
|
+
*
|
|
21
|
+
* @default Object.is
|
|
22
|
+
*/
|
|
23
|
+
readonly equals?: null | Equal<TValue>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A hook reads an {@link signal} value whenever it writes but enqueues a re-render only when the resulting value is different from the previous.
|
|
27
|
+
*
|
|
28
|
+
* @template TValue the type of the {@link Signal} value.
|
|
29
|
+
*
|
|
30
|
+
* @param signal anything that implements the {@link ReadableSignal} interface.
|
|
31
|
+
*
|
|
32
|
+
* @returns the {@link signal}'s value.
|
|
33
|
+
*
|
|
34
|
+
* @version 1.0.0
|
|
35
|
+
*/
|
|
36
|
+
declare function useComputed<TValue>(signal: ReadableSignal<TValue>): TValue;
|
|
37
|
+
/**
|
|
38
|
+
* A hook that executes the {@link compute} function whenever any of the involved {@link Signal}s' values update but enqueues a re-render only when the resulting value is different from the previous.
|
|
39
|
+
*
|
|
40
|
+
* @template TResult the type of the {@link compute} result.
|
|
41
|
+
*
|
|
42
|
+
* @param compute a function that provides {@link Monitor} as the first argument and subscribes to all {@link Signal}s calling the {@link Signal.read} method inside the function.
|
|
43
|
+
* @param dependencies optional array of dependencies of the {@link compute} function. If not defined, the {@link compute} function is called on every render.
|
|
44
|
+
* @param options optional {@link UseComputedOptions}.
|
|
45
|
+
* @param options.equals the {@link Equal} function that determines whether or not the {@link compute} result is different from the previous one. Defaults to {@link Object.is}.
|
|
46
|
+
*
|
|
47
|
+
* @returns the {@link compute} function result.
|
|
48
|
+
*
|
|
49
|
+
* @version 1.0.0
|
|
50
|
+
*/
|
|
51
|
+
declare function useComputed<TResult>(compute: (monitor: Monitor) => TResult, dependencies?: DependencyList, options?: UseComputedOptions<TResult>): TResult;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A hook that returns a {@link Monitor}.
|
|
55
|
+
*
|
|
56
|
+
* @returns A {@link Monitor} instance.
|
|
57
|
+
*
|
|
58
|
+
* @version 1.0.0
|
|
59
|
+
*/
|
|
60
|
+
declare function useMonitor(): Monitor;
|
|
61
|
+
|
|
62
|
+
export { type DependencyList, type UseComputedOptions, useComputed, useMonitor };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{useCallback as useCallback2,useDebugValue}from"react";function isStrictEqual(e,t){return Object.is(e,t)}import{MonitorFactory}from"@owanturist/signal/monitor-factory";import{useCallback}from"react";import{useSyncExternalStoreWithSelector}from"use-sync-external-store/with-selector";import{useRef}from"react";function useCreateMonitor(e,t){const[r,o]=function(e){const t=useRef(null);return t.current??(t.current={_:e()}),t.current._}(()=>{const e=new MonitorFactory;return[()=>e.create,t=>e.connect(t)]}),u=useCallback(t=>e(t()),[e]);return useSyncExternalStoreWithSelector(o,r,r,u,t)}import{useRef as useRef2}from"react";function useComputed(e,t,r){const o=useCreateMonitor(useCallback2(t=>"function"==typeof e?e(t):e.read(t),t??[e]),function(e){const t=useRef2({_:e,t:(...e)=>t.current._(...e)});return t.current._=e,t.current.t}((e,t)=>(r?.equals??isStrictEqual)(e,t)));return useDebugValue(o),o}function identity(e){return e}function useMonitor(){return useCreateMonitor(identity)}export{useComputed,useMonitor};//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/use-computed.ts","../../tools/src/is-function.ts","../../tools/src/is-strict-equal.ts","../src/_internal/use-create-monitor.ts","../src/_internal/use-permanent.ts","../src/_internal/use-handler.ts","../../tools/src/identity.ts","../src/use-monitor.ts"],"sourcesContent":["import type { Equal, Monitor, ReadableSignal, Signal } from \"@owanturist/signal\"\nimport { useCallback, useDebugValue } from \"react\"\n\nimport { isFunction } from \"~/tools/is-function\"\nimport { isStrictEqual } from \"~/tools/is-strict-equal\"\n\nimport type { DependencyList } from \"./dependency-list\"\nimport { useCreateMonitor } from \"./_internal/use-create-monitor\"\nimport { useHandler } from \"./_internal/use-handler\"\n\n/**\n * The options for the {@link useComputed} hook.\n *\n * @template TValue the type of the resulting value.\n *\n * @version 1.0.0\n */\ninterface UseComputedOptions<TValue> {\n /**\n * The equality check function determines whether or not the factory result is different.\n * If the factory result is different, a host component re-renders.\n * In some cases specifying the function leads to better performance because it prevents unnecessary updates.\n *\n * @default Object.is\n */\n readonly equals?: null | Equal<TValue>\n}\n\n/**\n * A hook reads an {@link signal} value whenever it writes but enqueues a re-render only when the resulting value is different from the previous.\n *\n * @template TValue the type of the {@link Signal} value.\n *\n * @param signal anything that implements the {@link ReadableSignal} interface.\n *\n * @returns the {@link signal}'s value.\n *\n * @version 1.0.0\n */\nfunction useComputed<TValue>(signal: ReadableSignal<TValue>): TValue\n\n/**\n * A hook that executes the {@link compute} function whenever any of the involved {@link Signal}s' values update but enqueues a re-render only when the resulting value is different from the previous.\n *\n * @template TResult the type of the {@link compute} result.\n *\n * @param compute a function that provides {@link Monitor} as the first argument and subscribes to all {@link Signal}s calling the {@link Signal.read} method inside the function.\n * @param dependencies optional array of dependencies of the {@link compute} function. If not defined, the {@link compute} function is called on every render.\n * @param options optional {@link UseComputedOptions}.\n * @param options.equals the {@link Equal} function that determines whether or not the {@link compute} result is different from the previous one. Defaults to {@link Object.is}.\n *\n * @returns the {@link compute} function result.\n *\n * @version 1.0.0\n */\nfunction useComputed<TResult>(\n compute: (monitor: Monitor) => TResult,\n dependencies?: DependencyList,\n options?: UseComputedOptions<TResult>,\n): TResult\n\nfunction useComputed<TResult>(\n computeOrReadableSignal: ((monitor: Monitor) => TResult) | ReadableSignal<TResult>,\n dependencies?: DependencyList,\n options?: UseComputedOptions<TResult>,\n): TResult {\n const transform = useCallback(\n (monitor: Monitor) => {\n if (isFunction(computeOrReadableSignal)) {\n return computeOrReadableSignal(monitor)\n }\n\n return computeOrReadableSignal.read(monitor)\n },\n // biome-ignore lint/correctness/useExhaustiveDependencies: pass dependencies as is or factory is the only dependency\n dependencies ?? [computeOrReadableSignal],\n )\n\n const equals = useHandler((prev: TResult, next: TResult) => {\n const fn = options?.equals ?? isStrictEqual\n\n return fn(prev, next)\n })\n\n const value = useCreateMonitor(transform, equals)\n\n useDebugValue(value)\n\n return value\n}\n\nexport type { UseComputedOptions }\nexport { useComputed }\n","type DefinitelyFunction<T> =\n // biome-ignore lint/complexity/noBannedTypes: use Function to ensure correct typing\n Extract<T, Function> extends never ? Function : Extract<T, Function>\n\nfunction isFunction<T>(\n // biome-ignore lint/complexity/noBannedTypes: use Function to ensure correct typing\n data: Function | T,\n): data is DefinitelyFunction<T> {\n return typeof data === \"function\"\n}\n\nexport { isFunction }\n","function isStrictEqual<T>(left: T, right: T): boolean {\n return Object.is(left, right)\n}\n\nexport { isStrictEqual }\n","import type { Monitor } from \"@owanturist/signal\"\nimport { MonitorFactory } from \"@owanturist/signal/monitor-factory\"\nimport { useCallback } from \"react\"\nimport { useSyncExternalStoreWithSelector } from \"use-sync-external-store/with-selector\"\n\nimport { usePermanent } from \"./use-permanent\"\n\nfunction useCreateMonitor<T>(\n transform: (monitor: Monitor) => T,\n equals?: (left: T, right: T) => boolean,\n): T {\n const [selectCreateMonitor, connect] = usePermanent(() => {\n const factory = new MonitorFactory()\n\n return [\n //\n () => factory.create,\n (emit: VoidFunction) => factory.connect(emit),\n ]\n })\n\n const select = useCallback((create: () => Monitor) => transform(create()), [transform])\n\n return useSyncExternalStoreWithSelector(\n connect,\n selectCreateMonitor,\n selectCreateMonitor,\n select,\n equals,\n )\n}\n\nexport { useCreateMonitor }\n","import { useRef } from \"react\"\n\nfunction usePermanent<TValue>(init: () => TValue): TValue {\n /**\n * According to the official React documentation\n * it is ok to write to the ref object during initialization.\n *\n * > Normally, writing or reading ref.current during render is not allowed.\n * > However, it’s fine in this case because the result is always the same,\n * > and the condition only executes during initialization so it’s fully predictable.\n *\n * @link https://react.dev/reference/react/useRef#avoiding-recreating-the-ref-contents\n */\n const ref = useRef<null | { _value: TValue }>(null)\n\n ref.current ??= { _value: init() }\n\n return ref.current._value\n}\n\nexport { usePermanent }\n","import { useRef } from \"react\"\n\nfunction useHandler<TArgs extends ReadonlyArray<unknown>, TResult>(\n handler: (...args: TArgs) => TResult,\n): (...args: TArgs) => TResult {\n /**\n * Despise React official documentation pointing out:\n *\n * > Do not write or read ref.current during rendering, except for initialization.\n * > This makes your component’s behavior unpredictable.\n *\n * While technically useLatest does write to ref.current during rendering (after the initial one), it's a widely accepted pattern because:\n * - It doesn't trigger re-renders.\n * - The side effect is predictable and serves a specific purpose (always having the latest value).\n * - It helps solve the common \"stale closure\" problem.\n * This pattern is recommended and used in many React resources despite the general warning in the useRef documentation.\n * The key is that the update to the ref doesn't directly influence the rendering output of the component in the same render cycle.\n *\n * @link https://react.dev/reference/react/useRef#caveats\n * @link https://react.dev/reference/react/useRef#avoiding-recreating-the-ref-contents\n */\n\n const ref = useRef({\n _value: handler,\n _getter: (...args: TArgs) => ref.current._value(...args),\n })\n\n ref.current._value = handler\n\n return ref.current._getter\n}\n\nexport { useHandler }\n","function identity<T>(value: T): T {\n return value\n}\n\nexport { identity }\n","import type { Monitor } from \"@owanturist/signal\"\n\nimport { identity } from \"~/tools/identity\"\n\nimport { useCreateMonitor } from \"./_internal/use-create-monitor\"\n\n/**\n * A hook that returns a {@link Monitor}.\n *\n * @returns A {@link Monitor} instance.\n *\n * @version 1.0.0\n */\nfunction useMonitor(): Monitor {\n return useCreateMonitor(identity)\n}\n\nexport { useMonitor }\n"],"mappings":";AACA,SAAS,eAAAA,cAAa,qBAAqB;;;ACG3C,SAAS,WAEP,MAC+B;AAC/B,SAAO,OAAO,SAAS;AACzB;;;ACTA,SAAS,cAAiB,MAAS,OAAmB;AACpD,SAAO,OAAO,GAAG,MAAM,KAAK;AAC9B;;;ACDA,SAAS,sBAAsB;AAC/B,SAAS,mBAAmB;AAC5B,SAAS,wCAAwC;;;ACHjD,SAAS,cAAc;AAEvB,SAAS,aAAqB,MAA4B;AAWxD,QAAM,MAAM,OAAkC,IAAI;AAElD,MAAI,YAAJ,IAAI,UAAY,EAAE,QAAQ,KAAK,EAAE;AAEjC,SAAO,IAAI,QAAQ;AACrB;;;ADXA,SAAS,iBACP,WACA,QACG;AACH,QAAM,CAAC,qBAAqB,OAAO,IAAI,aAAa,MAAM;AACxD,UAAM,UAAU,IAAI,eAAe;AAEnC,WAAO;AAAA;AAAA,MAEL,MAAM,QAAQ;AAAA,MACd,CAAC,SAAuB,QAAQ,QAAQ,IAAI;AAAA,IAC9C;AAAA,EACF,CAAC;AAED,QAAM,SAAS,YAAY,CAAC,WAA0B,UAAU,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;AAEtF,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AE9BA,SAAS,UAAAC,eAAc;AAEvB,SAAS,WACP,SAC6B;AAkB7B,QAAM,MAAMA,QAAO;AAAA,IACjB,QAAQ;AAAA,IACR,SAAS,IAAI,SAAgB,IAAI,QAAQ,OAAO,GAAG,IAAI;AAAA,EACzD,CAAC;AAED,MAAI,QAAQ,SAAS;AAErB,SAAO,IAAI,QAAQ;AACrB;;;AL+BA,SAAS,YACP,yBACA,cACA,SACS;AACT,QAAM,YAAYC;AAAA,IAChB,CAAC,YAAqB;AACpB,UAAI,WAAW,uBAAuB,GAAG;AACvC,eAAO,wBAAwB,OAAO;AAAA,MACxC;AAEA,aAAO,wBAAwB,KAAK,OAAO;AAAA,IAC7C;AAAA;AAAA,IAEA,gBAAgB,CAAC,uBAAuB;AAAA,EAC1C;AAEA,QAAM,SAAS,WAAW,CAAC,MAAe,SAAkB;AAC1D,UAAM,KAAK,SAAS,UAAU;AAE9B,WAAO,GAAG,MAAM,IAAI;AAAA,EACtB,CAAC;AAED,QAAM,QAAQ,iBAAiB,WAAW,MAAM;AAEhD,gBAAc,KAAK;AAEnB,SAAO;AACT;;;AMzFA,SAAS,SAAY,OAAa;AAChC,SAAO;AACT;;;ACWA,SAAS,aAAsB;AAC7B,SAAO,iBAAiB,QAAQ;AAClC;","names":["useCallback","useRef","useCallback"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owanturist/signal-react",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"description": "The clean and natural React state management",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
7
7
|
"module": "./dist/index.js",
|
|
@@ -28,10 +28,6 @@
|
|
|
28
28
|
},
|
|
29
29
|
"repository": "https://github.com/owanturist/signal/tree/master/packages/signal-react",
|
|
30
30
|
"homepage": "https://github.com/owanturist/signal/packages/signal-react#readme",
|
|
31
|
-
"scripts": {
|
|
32
|
-
"typecheck": "tsc --noEmit",
|
|
33
|
-
"build": "tsup src/index.ts --config ../../tsup.config.ts"
|
|
34
|
-
},
|
|
35
31
|
"dependencies": {
|
|
36
32
|
"use-sync-external-store": "^1.6.0"
|
|
37
33
|
},
|
|
@@ -40,7 +36,6 @@
|
|
|
40
36
|
"react": "^18.0.0 || ^19.0.0"
|
|
41
37
|
},
|
|
42
38
|
"devDependencies": {
|
|
43
|
-
"@owanturist/signal": "workspace:*",
|
|
44
39
|
"@testing-library/jest-dom": "^6.9.1",
|
|
45
40
|
"@testing-library/react": "^16.3.2",
|
|
46
41
|
"@types/react": "^19.2.10",
|
|
@@ -49,6 +44,11 @@
|
|
|
49
44
|
"react": "^19.2.4",
|
|
50
45
|
"react-dom": "^19.2.4",
|
|
51
46
|
"vite-tsconfig-paths": "^6.0.5",
|
|
52
|
-
"vitest": "^4.0.18"
|
|
47
|
+
"vitest": "^4.0.18",
|
|
48
|
+
"@owanturist/signal": "0.1.0"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"typecheck": "tsc --noEmit",
|
|
52
|
+
"build": "tsup src/index.ts --config ../../tsup.config.ts"
|
|
53
53
|
}
|
|
54
|
-
}
|
|
54
|
+
}
|