@electric-sql/react 0.0.5 → 0.0.7
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/dist/react-hooks.d.ts +9 -4
- package/dist/react-hooks.js +36 -21
- package/dist/react-hooks.js.map +1 -1
- package/dist/react-hooks.jsx +24 -22
- package/package.json +4 -2
package/dist/react-hooks.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { JsonSerializable, Shape, ShapeStream, ShapeStreamOptions } from '@electric-sql/next';
|
|
1
2
|
import React from 'react';
|
|
2
|
-
import { Shape, ShapeStream, ShapeStreamOptions, JsonSerializable } from '@electric-sql/next';
|
|
3
3
|
interface ShapeContextType {
|
|
4
4
|
getShape: (shapeStream: ShapeStream) => Shape;
|
|
5
5
|
getShapeStream: (options: ShapeStreamOptions) => ShapeStream;
|
|
@@ -16,9 +16,11 @@ export declare function useShapeContext(): ShapeContextType;
|
|
|
16
16
|
interface UseShapeResult {
|
|
17
17
|
/**
|
|
18
18
|
* The array of rows that make up the Shape.
|
|
19
|
-
* @type {JsonSerializable}
|
|
19
|
+
* @type {{ [key: string]: JsonSerializable }[]}
|
|
20
20
|
*/
|
|
21
|
-
data:
|
|
21
|
+
data: {
|
|
22
|
+
[key: string]: JsonSerializable;
|
|
23
|
+
}[];
|
|
22
24
|
/**
|
|
23
25
|
* The Shape instance used by this useShape
|
|
24
26
|
* @type(Shape)
|
|
@@ -31,5 +33,8 @@ interface UseShapeResult {
|
|
|
31
33
|
*/
|
|
32
34
|
isUpToDate: boolean;
|
|
33
35
|
}
|
|
34
|
-
|
|
36
|
+
interface UseShapeOptions<Selection> extends ShapeStreamOptions {
|
|
37
|
+
selector?: (value: UseShapeResult) => Selection;
|
|
38
|
+
}
|
|
39
|
+
export declare function useShape<Selection = UseShapeResult>({ selector, ...options }: UseShapeOptions<Selection>): Selection;
|
|
35
40
|
export {};
|
package/dist/react-hooks.js
CHANGED
|
@@ -33,11 +33,12 @@ var __async = (__this, __arguments, generator) => {
|
|
|
33
33
|
step((generator = generator.apply(__this, __arguments)).next());
|
|
34
34
|
});
|
|
35
35
|
};
|
|
36
|
-
import React, { createContext, useEffect, useContext, useState } from "react";
|
|
37
36
|
import {
|
|
38
37
|
Shape,
|
|
39
38
|
ShapeStream
|
|
40
39
|
} from "@electric-sql/next";
|
|
40
|
+
import React, { createContext, useCallback, useContext, useRef } from "react";
|
|
41
|
+
import { useSyncExternalStoreWithSelector } from "use-sync-external-store/with-selector";
|
|
41
42
|
const ShapesContext = createContext(null);
|
|
42
43
|
const streamCache = /* @__PURE__ */ new Map();
|
|
43
44
|
const shapeCache = /* @__PURE__ */ new Map();
|
|
@@ -91,31 +92,45 @@ function useShapeContext() {
|
|
|
91
92
|
}
|
|
92
93
|
return context;
|
|
93
94
|
}
|
|
94
|
-
function
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
function shapeSubscribe(shape, callback) {
|
|
96
|
+
const unsubscribe = shape.subscribe(callback);
|
|
97
|
+
return () => {
|
|
98
|
+
unsubscribe();
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function parseShapeData(shape) {
|
|
102
|
+
return {
|
|
99
103
|
data: [...shape.valueSync.values()],
|
|
100
104
|
isUpToDate: shape.isUpToDate,
|
|
101
105
|
isError: shape.error !== false,
|
|
102
106
|
shape,
|
|
103
107
|
error: shape.error
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
const identity = (arg) => arg;
|
|
111
|
+
function useShape(_a) {
|
|
112
|
+
var _b = _a, {
|
|
113
|
+
selector = identity
|
|
114
|
+
} = _b, options = __objRest(_b, [
|
|
115
|
+
"selector"
|
|
116
|
+
]);
|
|
117
|
+
const { getShape: getShape2, getShapeStream: getShapeStream2 } = useShapeContext();
|
|
118
|
+
const shapeStream = getShapeStream2(options);
|
|
119
|
+
const shape = getShape2(shapeStream);
|
|
120
|
+
const latestShapeData = useRef(parseShapeData(shape));
|
|
121
|
+
const getSnapshot = React.useCallback(() => latestShapeData.current, []);
|
|
122
|
+
const shapeData = useSyncExternalStoreWithSelector(
|
|
123
|
+
useCallback(
|
|
124
|
+
(onStoreChange) => shapeSubscribe(shape, () => {
|
|
125
|
+
latestShapeData.current = parseShapeData(shape);
|
|
126
|
+
onStoreChange();
|
|
127
|
+
}),
|
|
128
|
+
[shape]
|
|
129
|
+
),
|
|
130
|
+
getSnapshot,
|
|
131
|
+
getSnapshot,
|
|
132
|
+
selector
|
|
133
|
+
);
|
|
119
134
|
return shapeData;
|
|
120
135
|
}
|
|
121
136
|
export {
|
package/dist/react-hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/react-hooks.tsx"],"sourcesContent":["import React, { createContext,
|
|
1
|
+
{"version":3,"sources":["../src/react-hooks.tsx"],"sourcesContent":["import {\n JsonSerializable,\n Shape,\n ShapeStream,\n ShapeStreamOptions,\n} from '@electric-sql/next'\nimport React, { createContext, useCallback, useContext, useRef } from 'react'\nimport { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector'\n\ninterface ShapeContextType {\n getShape: (shapeStream: ShapeStream) => Shape\n getShapeStream: (options: ShapeStreamOptions) => ShapeStream\n}\n\n// Create a Context\nconst ShapesContext = createContext<ShapeContextType | null>(null)\n\nconst streamCache = new Map<string, ShapeStream>()\nconst shapeCache = new Map<ShapeStream, Shape>()\n\nexport async function preloadShape(\n options: ShapeStreamOptions\n): Promise<Shape> {\n const shapeStream = getShapeStream(options)\n const shape = getShape(shapeStream)\n await shape.value\n return shape\n}\n\nexport function sortedOptionsHash(options: ShapeStreamOptions): string {\n const shapeDef = JSON.stringify(\n options.shape,\n Object.keys(options.shape).sort()\n )\n // eslint-disable-next-line\n const { shape, ...optionsWithoutShapeDef } = options\n const allOptions = JSON.stringify(\n optionsWithoutShapeDef,\n Object.keys(options).sort()\n )\n const shapeHash = shapeDef + allOptions\n\n return shapeHash\n}\n\nexport function getShapeStream(options: ShapeStreamOptions): ShapeStream {\n const shapeHash = sortedOptionsHash(options)\n\n // If the stream is already cached, return\n if (streamCache.has(shapeHash)) {\n // Return the ShapeStream\n return streamCache.get(shapeHash)!\n } else {\n const newShapeStream = new ShapeStream(options)\n\n streamCache.set(shapeHash, newShapeStream)\n\n // Return the created shape\n return newShapeStream\n }\n}\n\nexport function getShape(shapeStream: ShapeStream): Shape {\n // If the stream is already cached, return\n if (shapeCache.has(shapeStream)) {\n // Return the ShapeStream\n return shapeCache.get(shapeStream)!\n } else {\n const newShape = new Shape(shapeStream)\n\n shapeCache.set(shapeStream, newShape)\n\n // Return the created shape\n return newShape\n }\n}\n\ninterface ShapeProviderProps {\n children: React.ReactNode\n}\n\n// Shapes Provider Component\nexport function ShapesProvider({ children }: ShapeProviderProps): JSX.Element {\n // Provide the context value\n return (\n <ShapesContext.Provider value={{ getShape, getShapeStream }}>\n {children}\n </ShapesContext.Provider>\n )\n}\n\nexport function useShapeContext() {\n const context = useContext(ShapesContext)\n if (!context) {\n throw new Error(`useShapeContext must be used within a ShapeProvider`)\n }\n return context\n}\n\ninterface UseShapeResult {\n /**\n * The array of rows that make up the Shape.\n * @type {{ [key: string]: JsonSerializable }[]}\n */\n data: { [key: string]: JsonSerializable }[]\n /**\n * The Shape instance used by this useShape\n * @type(Shape)\n */\n shape: Shape\n error: Shape[`error`]\n isError: boolean\n /**\n * Has the ShapeStream caught up with the replication log from Postgres.\n */\n isUpToDate: boolean\n}\n\nfunction shapeSubscribe(shape: Shape, callback: () => void) {\n const unsubscribe = shape.subscribe(callback)\n return () => {\n unsubscribe()\n }\n}\n\nfunction parseShapeData(shape: Shape): UseShapeResult {\n return {\n data: [...shape.valueSync.values()],\n isUpToDate: shape.isUpToDate,\n isError: shape.error !== false,\n shape,\n error: shape.error,\n }\n}\n\nconst identity = (arg: unknown) => arg\n\ninterface UseShapeOptions<Selection> extends ShapeStreamOptions {\n selector?: (value: UseShapeResult) => Selection\n}\n\nexport function useShape<Selection = UseShapeResult>({\n selector = identity as never,\n ...options\n}: UseShapeOptions<Selection>): Selection {\n const { getShape, getShapeStream } = useShapeContext()\n const shapeStream = getShapeStream(options as ShapeStreamOptions)\n const shape = getShape(shapeStream)\n\n const latestShapeData = useRef(parseShapeData(shape))\n const getSnapshot = React.useCallback(() => latestShapeData.current, [])\n const shapeData = useSyncExternalStoreWithSelector(\n useCallback(\n (onStoreChange) =>\n shapeSubscribe(shape, () => {\n latestShapeData.current = parseShapeData(shape)\n onStoreChange()\n }),\n [shape]\n ),\n getSnapshot,\n getSnapshot,\n selector\n )\n\n return shapeData\n}\n"],"mappings":"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","names":["getShape","getShapeStream"]}
|
package/dist/react-hooks.jsx
CHANGED
|
@@ -18,8 +18,9 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
18
18
|
}
|
|
19
19
|
return t;
|
|
20
20
|
};
|
|
21
|
-
import React, { createContext, useEffect, useContext, useState } from 'react';
|
|
22
21
|
import { Shape, ShapeStream, } from '@electric-sql/next';
|
|
22
|
+
import React, { createContext, useCallback, useContext, useRef } from 'react';
|
|
23
|
+
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector';
|
|
23
24
|
// Create a Context
|
|
24
25
|
const ShapesContext = createContext(null);
|
|
25
26
|
const streamCache = new Map();
|
|
@@ -81,31 +82,32 @@ export function useShapeContext() {
|
|
|
81
82
|
}
|
|
82
83
|
return context;
|
|
83
84
|
}
|
|
84
|
-
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
85
|
+
function shapeSubscribe(shape, callback) {
|
|
86
|
+
const unsubscribe = shape.subscribe(callback);
|
|
87
|
+
return () => {
|
|
88
|
+
unsubscribe();
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function parseShapeData(shape) {
|
|
92
|
+
return {
|
|
89
93
|
data: [...shape.valueSync.values()],
|
|
90
94
|
isUpToDate: shape.isUpToDate,
|
|
91
95
|
isError: shape.error !== false,
|
|
92
96
|
shape,
|
|
93
97
|
error: shape.error,
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
};
|
|
109
|
-
}, []);
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const identity = (arg) => arg;
|
|
101
|
+
export function useShape(_a) {
|
|
102
|
+
var { selector = identity } = _a, options = __rest(_a, ["selector"]);
|
|
103
|
+
const { getShape, getShapeStream } = useShapeContext();
|
|
104
|
+
const shapeStream = getShapeStream(options);
|
|
105
|
+
const shape = getShape(shapeStream);
|
|
106
|
+
const latestShapeData = useRef(parseShapeData(shape));
|
|
107
|
+
const getSnapshot = React.useCallback(() => latestShapeData.current, []);
|
|
108
|
+
const shapeData = useSyncExternalStoreWithSelector(useCallback((onStoreChange) => shapeSubscribe(shape, () => {
|
|
109
|
+
latestShapeData.current = parseShapeData(shape);
|
|
110
|
+
onStoreChange();
|
|
111
|
+
}), [shape]), getSnapshot, getSnapshot, selector);
|
|
110
112
|
return shapeData;
|
|
111
113
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@electric-sql/react",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "React hooks for ElectricSQL",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -18,12 +18,14 @@
|
|
|
18
18
|
},
|
|
19
19
|
"homepage": "https://next.electric-sql.com",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"
|
|
21
|
+
"use-sync-external-store": "^1.2.2",
|
|
22
|
+
"@electric-sql/next": "0.0.7"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@testing-library/react": "^16.0.0",
|
|
25
26
|
"@types/pg": "^8.11.6",
|
|
26
27
|
"@types/react": "^18.3.3",
|
|
28
|
+
"@types/use-sync-external-store": "^0.0.6",
|
|
27
29
|
"@types/uuid": "^10.0.0",
|
|
28
30
|
"@typescript-eslint/eslint-plugin": "^7.14.1",
|
|
29
31
|
"@typescript-eslint/parser": "^7.14.1",
|