@virtuoso.dev/reactive-engine-react 0.0.2

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.
@@ -0,0 +1,197 @@
1
+ import { Engine } from '@virtuoso.dev/reactive-engine-core';
2
+ import { Inp } from '@virtuoso.dev/reactive-engine-core';
3
+ import { NodeRef } from '@virtuoso.dev/reactive-engine-core';
4
+ import { Out } from '@virtuoso.dev/reactive-engine-core';
5
+ import * as React_2 from 'react';
6
+
7
+ /**
8
+ * The context that provides an engine instance used by the built-in hooks. Instantiated by {@link EngineProvider}.
9
+ *
10
+ * @category React Hooks and Components
11
+ * @function
12
+ */
13
+ export declare const EngineContext: React_2.Context<Engine | null>;
14
+
15
+ /**
16
+ * A provider that instantiates and provides an {@link Engine} instance that's used by the built-in hooks.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * import { Cell, useCellValue, e, EngineProvider } from '@virtuoso.dev/reactive-engine'
21
+ * const cell$ = Cell(0)
22
+ *
23
+ * function MyComponent() {
24
+ * const cell = useCellValue(cell$)
25
+ * return <div>{cell}</div>
26
+ * }
27
+ *
28
+ * export default function App() {
29
+ * return <EngineProvider><MyComponent /></EngineProvider>
30
+ * }
31
+ * ```
32
+ *
33
+ * @category React Hooks and Components
34
+ * @function
35
+ */
36
+ export declare const EngineProvider: React_2.FC<EngineProviderProps>;
37
+
38
+ /**
39
+ * @inline
40
+ * @category React Hooks and Components
41
+ */
42
+ export declare interface EngineProviderProps {
43
+ /**
44
+ * The children to render.
45
+ */
46
+ children: React_2.ReactNode;
47
+ /**
48
+ * Optional stable ID for storage namespacing. Use this for multi-engine apps to prevent storage key conflicts.
49
+ */
50
+ engineId?: string;
51
+ /**
52
+ * The initial values to set in the engine.
53
+ */
54
+ initWith?: Record<symbol, unknown>;
55
+ /**
56
+ * The values to update in the engine on each render.
57
+ */
58
+ updateWith?: Record<symbol, unknown>;
59
+ }
60
+
61
+ /**
62
+ * Returns a tuple of the current value of a cell and a publisher function (similar to `useState`).
63
+ * The component re-renderes when the cell value changes.
64
+ *
65
+ * @remarks The reactive engine state management allows you to keep your state logic outside of your React components.
66
+ * Be careful not to use this hook too often alongside `useEffect` for example, as this means that you're losing the benefits of the reactive engine design.
67
+ *
68
+ * @param cell - The cell to use.
69
+ * @returns A tuple of the current value of the cell and a publisher function.
70
+ * @typeParam T - The type of values that the cell emits/accepts.
71
+ * @category React Hooks and Components
72
+ */
73
+ export declare function useCell<T>(cell: NodeRef<T>): [T, (value: T) => void];
74
+
75
+ /**
76
+ * Gets the current value of the cell. The component is re-rendered when the cell value changes.
77
+ *
78
+ * @remarks If you need the values of multiple nodes from the engine and those nodes might change in the same computiation, you can {@link useCellValues} to reduce re-renders.
79
+ *
80
+ * @returns The current value of the cell.
81
+ * @typeParam T - the type of the value that the cell caries.
82
+ * @param cell - The cell to use.
83
+ *
84
+ * @example
85
+ * ```tsx
86
+ * const cell$ = Cell(0)
87
+ * //...
88
+ * function MyComponent() {
89
+ * const cell = useCellValue(cell$)
90
+ * return <div>{cell}</div>
91
+ * }
92
+ * ```
93
+ * @category React Hooks and Components
94
+ * @function
95
+ */
96
+ export declare const useCellValue: typeof useCellValueWithStore;
97
+
98
+ /**
99
+ * Returns the up-to-date values of the passed cells.
100
+ * The component is re-rendered each time any of the cells emits a new value.
101
+ * @category React Hooks and Components
102
+ * @returns Correclty typed array with the current values of the passed cells.
103
+ *
104
+ * @remarks This hook works only with cells, don't pass streams or triggers.
105
+ *
106
+ * @example
107
+ * ```tsx
108
+ * import { Cell, useCellValues }
109
+ * const foo$ = Cell('foo')
110
+ * const bar$ = Cell('bar')
111
+ * // ...
112
+ * // The component should be wrapped in an EngineProvider.
113
+ * function MyComponent() {
114
+ * const [foo, bar] = useCellValues(foo$, bar$)
115
+ * return <div>{foo} - {bar}</div>
116
+ * }
117
+ * ```
118
+ */
119
+ export declare function useCellValues(...cells: Out[]): unknown[];
120
+
121
+ /** @hidden */
122
+ export declare function useCellValues<T1>(...cells: [Out<T1>]): [T1];
123
+
124
+ /** @hidden */
125
+ export declare function useCellValues<T1, T2>(...cells: [Out<T1>, Out<T2>]): [T1, T2];
126
+
127
+ /** @hidden */
128
+ export declare function useCellValues<T1, T2, T3>(...cells: [Out<T1>, Out<T2>, Out<T3>]): [T1, T2, T3];
129
+
130
+ /** @hidden */
131
+ export declare function useCellValues<T1, T2, T3, T4>(...cells: [Out<T1>, Out<T2>, Out<T3>, Out<T4>]): [T1, T2, T3, T4];
132
+
133
+ /** @hidden */
134
+ export declare function useCellValues<T1, T2, T3, T4, T5>(...cells: [Out<T1>, Out<T2>, Out<T3>, Out<T4>, Out<T5>]): [T1, T2, T3, T4, T5];
135
+
136
+ /** @hidden */
137
+ export declare function useCellValues<T1, T2, T3, T4, T5, T6>(...cells: [Out<T1>, Out<T2>, Out<T3>, Out<T4>, Out<T5>, Out<T6>]): [T1, T2, T3, T4, T5, T6];
138
+
139
+ /** @hidden */
140
+ export declare function useCellValues<T1, T2, T3, T4, T5, T6, T7>(...cells: [Out<T1>, Out<T2>, Out<T3>, Out<T4>, Out<T5>, Out<T6>, Out<T7>]): [T1, T2, T3, T4, T5, T6, T7];
141
+
142
+ /** @hidden */
143
+ export declare function useCellValues<T1, T2, T3, T4, T5, T6, T7, T8>(...cells: [Out<T1>, Out<T2>, Out<T3>, Out<T4>, Out<T5>, Out<T6>, Out<T7>, Out<T8>]): [T1, T2, T3, T4, T5, T6, T7, T8];
144
+
145
+ /** @hidden */
146
+ export declare function useCellValues<T1, T2, T3, T4, T5, T6, T7, T8, T9>(...cells: [Out<T1>, Out<T2>, Out<T3>, Out<T4>, Out<T5>, Out<T6>, Out<T7>, Out<T8>, Out<T9>]): [T1, T2, T3, T4, T5, T6, T7, T8, T9];
147
+
148
+ /** @hidden */
149
+ export declare function useCellValues<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(...cells: [Out<T1>, Out<T2>, Out<T3>, Out<T4>, Out<T5>, Out<T6>, Out<T7>, Out<T8>, Out<T9>, Out<T10>]): [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10];
150
+
151
+ /** @hidden */
152
+ export declare function useCellValues<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(...cells: [Out<T1>, Out<T2>, Out<T3>, Out<T4>, Out<T5>, Out<T6>, Out<T7>, Out<T8>, Out<T9>, Out<T10>, Out<T11>]): [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11];
153
+
154
+ /** @hidden */
155
+ export declare function useCellValues<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(...cells: [Out<T1>, Out<T2>, Out<T3>, Out<T4>, Out<T5>, Out<T6>, Out<T7>, Out<T8>, Out<T9>, Out<T10>, Out<T11>, Out<T12>]): [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12];
156
+
157
+ /** @hidden */
158
+ export declare function useCellValues<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(...cells: [Out<T1>, Out<T2>, Out<T3>, Out<T4>, Out<T5>, Out<T6>, Out<T7>, Out<T8>, Out<T9>, Out<T10>, Out<T11>, Out<T12>, Out<T13>]): [T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13];
159
+
160
+ declare function useCellValueWithStore<T>(cell: Out<T>): T;
161
+
162
+ /**
163
+ * Returns a reference to the current engine instance created in the {@link EngineProvider}.
164
+ *
165
+ * @remarks Accessing the engine instance directly in React is rarely needed.
166
+ * Use {@link useCellValue} and {@link usePublisher} to interact with the nodes.
167
+ * @category React Hooks and Components
168
+ */
169
+ export declare function useEngine(): Engine;
170
+
171
+ export declare const useIsomorphicLayoutEffect: typeof React_2.useLayoutEffect;
172
+
173
+ /**
174
+ * Returns a function that publishes its passed argument into the specified node.
175
+ * @param node$ - the node to publish in.
176
+ * @typeParam T - The type of values that the node accepts.
177
+ * @returns The publisher function for the passed `node$`.
178
+ *
179
+ * @example
180
+ * ```tsx
181
+ * import {Stream, e, usePublisher} from '@virtuoso.dev/reactive-engine'
182
+ *
183
+ * const stream$ = Stream<number>()
184
+ * e.sub(stream, (value) => console.log(`${value} was published in the stream`))
185
+ *
186
+ * //...
187
+ * function MyComponent() {
188
+ * const pub = usePublisher(stream);
189
+ * return <button onClick={() => pub(2)}>Push a value into the stream</button>
190
+ * }
191
+ * ```
192
+ *
193
+ * @category React Hooks and Components
194
+ */
195
+ export declare function usePublisher<T>(node$: Inp<T>): (value: T) => void;
196
+
197
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,63 @@
1
+ import { jsx as g } from "react/jsx-runtime";
2
+ import { Engine as m } from "@virtuoso.dev/reactive-engine-core";
3
+ import * as t from "react";
4
+ import C from "tiny-invariant";
5
+ const i = typeof document < "u" ? t.useLayoutEffect : t.useEffect, c = t.createContext(null);
6
+ function o() {
7
+ const e = t.useContext(c);
8
+ return C(e !== null, "useEngine must be used within an EngineProvider"), e;
9
+ }
10
+ function E(e) {
11
+ const n = o();
12
+ n.register(e);
13
+ const u = t.useCallback((r) => n.sub(e, r), [n, e]);
14
+ return t.useSyncExternalStore(
15
+ u,
16
+ () => n.getValue(e),
17
+ () => n.getValue(e)
18
+ );
19
+ }
20
+ function b(e) {
21
+ const n = o();
22
+ n.register(e);
23
+ const [u, r] = t.useState(() => n.getValue(e));
24
+ return i(() => n.sub(e, r), [n, e]), u;
25
+ }
26
+ const l = "useSyncExternalStore" in t ? E : b;
27
+ function d(...e) {
28
+ const n = o(), u = t.useMemo(() => n.combineCells(e), [n, ...e]);
29
+ return l(u);
30
+ }
31
+ function p(e) {
32
+ const n = o();
33
+ return n.register(e), t.useCallback(
34
+ (u) => {
35
+ n.pub(e, u);
36
+ },
37
+ [n, e]
38
+ );
39
+ }
40
+ function v(e) {
41
+ return [l(e), p(e)];
42
+ }
43
+ const y = ({ children: e, engineId: n, initWith: u, updateWith: r = {} }) => {
44
+ const [s, f] = t.useState(null);
45
+ return i(() => {
46
+ const a = new m(u, n);
47
+ return f(a), () => {
48
+ a.dispose();
49
+ };
50
+ }, [u, n]), i(() => {
51
+ s == null || s.pubIn(r);
52
+ }, [r, s]), s && /* @__PURE__ */ g(c.Provider, { value: s, children: e });
53
+ };
54
+ export {
55
+ c as EngineContext,
56
+ y as EngineProvider,
57
+ v as useCell,
58
+ l as useCellValue,
59
+ d as useCellValues,
60
+ o as useEngine,
61
+ i as useIsomorphicLayoutEffect,
62
+ p as usePublisher
63
+ };
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@virtuoso.dev/reactive-engine-react",
3
+ "private": false,
4
+ "sideEffects": false,
5
+ "type": "module",
6
+ "version": "0.0.2",
7
+ "module": "dist/index.js",
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "publishConfig": {
11
+ "access": "public",
12
+ "provenance": true
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/petyosi/react-virtuoso.git",
17
+ "directory": "packages/reactive-engine-react"
18
+ },
19
+ "license": "MIT",
20
+ "dependencies": {
21
+ "tiny-invariant": "^1.3.3"
22
+ },
23
+ "peerDependencies": {
24
+ "@virtuoso.dev/reactive-engine-core": ">=0.0.2",
25
+ "react": ">= 18",
26
+ "react-dom": ">= 18"
27
+ },
28
+ "devDependencies": {
29
+ "@testing-library/jest-dom": "^6.9.1",
30
+ "@testing-library/react": "^16.3.0",
31
+ "@types/node": "^22.10.1",
32
+ "@types/react": "^19.2.7",
33
+ "@types/react-dom": "^19.2.3",
34
+ "@vitest/browser": "^4.0.16",
35
+ "@vitest/browser-playwright": "^4.0.16",
36
+ "@vitejs/plugin-react": "^4.5.0",
37
+ "eslint": "^9.24.0",
38
+ "jsdom": "^27.4.0",
39
+ "playwright": "^1.33.0",
40
+ "prettier": "^3.5.3",
41
+ "react": "^19.2.3",
42
+ "react-dom": "^19.2.3",
43
+ "typescript": "~5.9.3",
44
+ "vite": "^6.4.1",
45
+ "vite-plugin-dts": "^4.5.4",
46
+ "vitest": "^4.0.16",
47
+ "vitest-browser-react": "^2.0.2",
48
+ "@virtuoso.dev/reactive-engine-core": "0.0.2",
49
+ "@virtuoso.dev/tooling": "0.1.0"
50
+ },
51
+ "files": [
52
+ "dist"
53
+ ],
54
+ "scripts": {
55
+ "build": "tsc && vite build",
56
+ "test": "vitest --run --passWithNoTests --browser.headless",
57
+ "lint": "eslint",
58
+ "typecheck": "tsgo --noEmit",
59
+ "format": "prettier --write .",
60
+ "format:check": "prettier --check ."
61
+ }
62
+ }