@t8/react-store 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/README.md ADDED
File without changes
package/dist/index.js ADDED
@@ -0,0 +1,62 @@
1
+ // node_modules/@t8/store/src/isStore.ts
2
+ function isStore(x) {
3
+ return x !== null && typeof x === "object" && "onUpdate" in x && typeof x.onUpdate === "function" && "getState" in x && typeof x.getState === "function" && "setState" in x && typeof x.setState === "function";
4
+ }
5
+
6
+ // node_modules/@t8/store/src/Store.ts
7
+ var Store = class {
8
+ state;
9
+ callbacks;
10
+ constructor(data) {
11
+ this.state = data;
12
+ this.callbacks = [];
13
+ }
14
+ onUpdate(callback) {
15
+ this.callbacks.push(callback);
16
+ return () => {
17
+ for (let i = this.callbacks.length - 1; i >= 0; i--) {
18
+ if (this.callbacks[i] === callback) this.callbacks.splice(i, 1);
19
+ }
20
+ };
21
+ }
22
+ getState() {
23
+ return this.state;
24
+ }
25
+ setState(update) {
26
+ let prevState = this.state;
27
+ let nextState = update instanceof Function ? update(this.state) : update;
28
+ this.state = nextState;
29
+ for (let callback of this.callbacks) callback(nextState, prevState);
30
+ }
31
+ };
32
+
33
+ // src/useStore.ts
34
+ import { useEffect, useMemo, useRef, useState } from "react";
35
+ function useStore(store, shouldUpdate = true) {
36
+ if (!isStore(store)) throw new Error("'store' is not an instance of Store");
37
+ let [, setRevision] = useState(-1);
38
+ let initedRef = useRef(false);
39
+ let state = store.getState();
40
+ let setState = useMemo(() => store.setState.bind(store), [store]);
41
+ useEffect(() => {
42
+ if (!shouldUpdate) return;
43
+ let unsubscribe = store.onUpdate((nextState, prevState) => {
44
+ if (typeof shouldUpdate !== "function" || shouldUpdate(nextState, prevState))
45
+ setRevision(Math.random());
46
+ });
47
+ if (!initedRef.current) {
48
+ initedRef.current = true;
49
+ setRevision(Math.random());
50
+ }
51
+ return () => {
52
+ unsubscribe();
53
+ initedRef.current = false;
54
+ };
55
+ }, [store, shouldUpdate]);
56
+ return [state, setState];
57
+ }
58
+ export {
59
+ Store,
60
+ isStore,
61
+ useStore
62
+ };
package/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from '@t8/store';
2
+ export * from './src/useStore';
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@t8/react-store",
3
+ "version": "0.1.0",
4
+ "description": "Straightforward and minimalist shared state management for React apps",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "build": "npx npm-run-all clean compile",
9
+ "clean": "node -e \"require('node:fs').rmSync('dist', {force: true, recursive: true});\"",
10
+ "compile": "npx esbuild index.ts --bundle --outdir=dist --platform=neutral --external:react",
11
+ "prepublishOnly": "npm run build",
12
+ "preversion": "npx npm-run-all shape build",
13
+ "shape": "npx codeshape"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/t8dev/react-store.git"
18
+ },
19
+ "keywords": [
20
+ "react",
21
+ "shared state",
22
+ "store"
23
+ ],
24
+ "author": "axtk",
25
+ "license": "ISC",
26
+ "peerDependencies": {
27
+ "react": ">=16.8"
28
+ },
29
+ "dependencies": {
30
+ "@t8/store": "^0.1.1"
31
+ },
32
+ "devDependencies": {
33
+ "@types/react": "^19.1.10"
34
+ }
35
+ }
@@ -0,0 +1,60 @@
1
+ import {isStore, type Store} from '@t8/store';
2
+ import {useEffect, useMemo, useRef, useState} from 'react';
3
+
4
+ export type SetStoreState<T> = Store<T>['setState'];
5
+ export type ShouldUpdateCallback<T> = (nextState: T, prevState: T) => boolean;
6
+ export type ShouldUpdate<T> = boolean | ShouldUpdateCallback<T>;
7
+
8
+ export function useStore<T>(
9
+ store: Store<T>,
10
+ /**
11
+ * Controls whether the component should be updated in response
12
+ * to the store updates.
13
+ *
14
+ * @defaultValue `true`
15
+ *
16
+ * Can be set to `false` when the component only requires the
17
+ * store state setter but not the state value itself, and so the
18
+ * component doesn't need to respond to updates in the store state.
19
+ *
20
+ * ```ts
21
+ * let [, setValue] = useStore(store, false);
22
+ * ```
23
+ *
24
+ * Can be set to a function `(nextState, prevState) => boolean` to
25
+ * make the component respond only to specific store state changes.
26
+ */
27
+ shouldUpdate: ShouldUpdate<T> = true,
28
+ ): [T, SetStoreState<T>] {
29
+ if (!isStore(store)) throw new Error("'store' is not an instance of Store");
30
+
31
+ let [, setRevision] = useState(-1);
32
+ let initedRef = useRef(false);
33
+
34
+ let state = store.getState();
35
+ let setState = useMemo(() => store.setState.bind(store), [store]);
36
+
37
+ useEffect(() => {
38
+ if (!shouldUpdate) return;
39
+
40
+ let unsubscribe = store.onUpdate((nextState, prevState) => {
41
+ if (
42
+ typeof shouldUpdate !== 'function' ||
43
+ shouldUpdate(nextState, prevState)
44
+ )
45
+ setRevision(Math.random());
46
+ });
47
+
48
+ if (!initedRef.current) {
49
+ initedRef.current = true;
50
+ setRevision(Math.random());
51
+ }
52
+
53
+ return () => {
54
+ unsubscribe();
55
+ initedRef.current = false;
56
+ };
57
+ }, [store, shouldUpdate]);
58
+
59
+ return [state, setState];
60
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,12 @@
1
+ {
2
+ "include": ["index.ts", "src/**/*"],
3
+ "compilerOptions": {
4
+ "lib": ["ESNext", "DOM"],
5
+ "target": "ESNext",
6
+ "outDir": "dist",
7
+ "moduleResolution": "node",
8
+ "strict": true,
9
+ "noUnusedLocals": true,
10
+ "noUnusedParameters": true
11
+ }
12
+ }