@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 +0 -0
- package/dist/index.js +62 -0
- package/index.ts +2 -0
- package/package.json +35 -0
- package/src/useStore.ts +60 -0
- package/tsconfig.json +12 -0
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
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
|
+
}
|
package/src/useStore.ts
ADDED
|
@@ -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