store init
This commit is contained in:
parent
b5dde4e823
commit
152fda350e
25
package.json
25
package.json
@ -8,12 +8,14 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "rollup -c -w",
|
"dev": "rollup -c -w",
|
||||||
|
"dev:lib": "rollup -c -w",
|
||||||
"build": "npm run clean && rollup -c",
|
"build": "npm run clean && rollup -c",
|
||||||
"build:app": "npm run build && rsync dist/* ../deploy/dist",
|
"build:app": "npm run build && rsync dist/* ../deploy/dist",
|
||||||
"clean": "rm -rf dist"
|
"clean": "rm -rf dist"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist",
|
||||||
|
"react/dist"
|
||||||
],
|
],
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"kevisual",
|
"kevisual",
|
||||||
@ -23,20 +25,21 @@
|
|||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"description": "",
|
"description": "",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^28.0.1",
|
"@rollup/plugin-commonjs": "^28.0.3",
|
||||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||||
"@rollup/plugin-typescript": "^12.1.1",
|
"@rollup/plugin-typescript": "^12.1.2",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"immer": "^10.1.1",
|
"immer": "^10.1.1",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"nanoid": "^5.0.9",
|
"nanoid": "^5.1.5",
|
||||||
"rollup": "^4.27.4",
|
"rollup": "^4.37.0",
|
||||||
"rollup-plugin-dts": "^6.1.1",
|
"rollup-plugin-dts": "^6.2.1",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.7.2",
|
"tsup": "^8.4.0",
|
||||||
"zustand": "^5.0.1"
|
"typescript": "^5.8.2",
|
||||||
|
"zustand": "^5.0.3"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
@ -65,6 +68,10 @@
|
|||||||
"./web": {
|
"./web": {
|
||||||
"import": "./dist/web.js",
|
"import": "./dist/web.js",
|
||||||
"require": "./dist/web.js"
|
"require": "./dist/web.js"
|
||||||
|
},
|
||||||
|
"./react": {
|
||||||
|
"import": "./react/dist/store-react.js",
|
||||||
|
"types": "./react/dist/index.d.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
23
react/package.json
Normal file
23
react/package.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"name": "@kevisual/store-react",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"dev:lib": "vite build --watch",
|
||||||
|
"build:lib": "vite build"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
||||||
|
"license": "MIT",
|
||||||
|
"packageManager": "pnpm@10.6.5",
|
||||||
|
"type": "module",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^19.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
|
"vite": "^6.2.3",
|
||||||
|
"vite-plugin-dts": "^4.5.3"
|
||||||
|
}
|
||||||
|
}
|
58
react/src/Store.tsx
Normal file
58
react/src/Store.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { StateCreator } from '../../src/store';
|
||||||
|
import { shallow, useShallow } from 'zustand/shallow';
|
||||||
|
import { useContextKey } from '../../src/web-context';
|
||||||
|
|
||||||
|
export const StoreContext = createContext<any>(null);
|
||||||
|
|
||||||
|
export const initStoreFn: StateCreator<any, [], [], any> = (set, get, store) => {
|
||||||
|
return {
|
||||||
|
mark: '123',
|
||||||
|
setMark: (mark: string) => set({ mark }),
|
||||||
|
info: 'info',
|
||||||
|
setInfo: (info) => set({ info }),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const StoreContextProvider = ({
|
||||||
|
children,
|
||||||
|
id,
|
||||||
|
stateCreator,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
id: string;
|
||||||
|
stateCreator?: StateCreator<any, [], [], any>;
|
||||||
|
}) => {
|
||||||
|
const store = useContextKey<any>('store');
|
||||||
|
if (!store) {
|
||||||
|
console.error('store not found');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const smStore = useMemo(() => {
|
||||||
|
return store.createIfNotExists(stateCreator || initStoreFn, id);
|
||||||
|
}, [id]);
|
||||||
|
const [state, setState] = useState(smStore);
|
||||||
|
useEffect(() => {
|
||||||
|
setState(smStore);
|
||||||
|
}, [smStore]);
|
||||||
|
return <StoreContext.Provider value={state}>{children}</StoreContext.Provider>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useStore = (selector?: any) => {
|
||||||
|
const store = useContext(StoreContext);
|
||||||
|
const allState = store.getState();
|
||||||
|
const selectedState = selector ? selector(allState) : allState;
|
||||||
|
const [state, setState] = useState(selectedState);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const unsubscribe = store.subscribe((newState: any) => {
|
||||||
|
const newSelectedState = selector ? selector(newState) : newState;
|
||||||
|
if (!shallow(state, newSelectedState)) {
|
||||||
|
setState(newSelectedState);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => unsubscribe();
|
||||||
|
}, [store, useShallow, state]);
|
||||||
|
|
||||||
|
return state;
|
||||||
|
};
|
83
react/src/UseStoreContext.tsx
Normal file
83
react/src/UseStoreContext.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { StateCreator } from '../../src/store';
|
||||||
|
import { shallow, useShallow } from 'zustand/shallow';
|
||||||
|
import { useContextKey } from '../../src/web-context';
|
||||||
|
|
||||||
|
export const initStoreFn: StateCreator<any, [], [], any> = (set, get, store) => {
|
||||||
|
return {
|
||||||
|
description: 'this is a blank store',
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export const useStoreContext = (id: string, stateCreator?: StateCreator<any, [], [], any>) => {
|
||||||
|
const StoreContext = createContext<any>(null);
|
||||||
|
const store = useContextKey<any>('store');
|
||||||
|
if (!store) {
|
||||||
|
console.error('store not found');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!stateCreator) {
|
||||||
|
console.error('stateCreator not found');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StoreContextProvider = ({ children, id, stateCreator }: { children: React.ReactNode; id: string; stateCreator?: StateCreator<any, [], [], any> }) => {
|
||||||
|
const smStore = useMemo(() => {
|
||||||
|
console.log('stateCreator', stateCreator);
|
||||||
|
return store.createIfNotExists(stateCreator || initStoreFn, id);
|
||||||
|
}, [id]);
|
||||||
|
const [state, setState] = useState(smStore);
|
||||||
|
useEffect(() => {
|
||||||
|
setState(smStore);
|
||||||
|
}, [smStore]);
|
||||||
|
console.log('value', smStore.getState());
|
||||||
|
// console.log('value', state);
|
||||||
|
return <StoreContext.Provider value={state}>{children}</StoreContext.Provider>;
|
||||||
|
};
|
||||||
|
const useStore = (selector?: any) => {
|
||||||
|
const store = useContext(StoreContext);
|
||||||
|
const allState = store.getState();
|
||||||
|
const selectedState = selector ? selector(allState) : allState;
|
||||||
|
const [state, setState] = useState(selectedState);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const unsubscribe = store.subscribe((newState: any) => {
|
||||||
|
const newSelectedState = selector ? selector(newState) : newState;
|
||||||
|
setState(newSelectedState);
|
||||||
|
});
|
||||||
|
return () => unsubscribe();
|
||||||
|
}, [store, useShallow, state]);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
// console.log('store', store);
|
||||||
|
// @ts-ignore
|
||||||
|
window.storeContext = {
|
||||||
|
// @ts-ignore
|
||||||
|
...window.storeContext,
|
||||||
|
[id]: {
|
||||||
|
StoreContext,
|
||||||
|
Provider: ({ children }: { children: React.ReactNode }) => {
|
||||||
|
return (
|
||||||
|
<StoreContextProvider id={id} stateCreator={stateCreator}>
|
||||||
|
{children}
|
||||||
|
</StoreContextProvider>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return () => {
|
||||||
|
// @ts-ignore
|
||||||
|
delete window.storeContext[id];
|
||||||
|
};
|
||||||
|
}, [id]);
|
||||||
|
return {
|
||||||
|
StoreContext,
|
||||||
|
Provider: ({ children }: { children: React.ReactNode }) => {
|
||||||
|
return (
|
||||||
|
<StoreContextProvider id={id} stateCreator={stateCreator}>
|
||||||
|
{children}
|
||||||
|
</StoreContextProvider>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
useStore,
|
||||||
|
};
|
||||||
|
};
|
1
react/src/index.ts
Normal file
1
react/src/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './Store';
|
43
react/tsconfig.json
Normal file
43
react/tsconfig.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": [
|
||||||
|
"ES2020",
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable"
|
||||||
|
],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
// "jsx": "react",
|
||||||
|
// "jsxFragmentFactory": "Fragment",
|
||||||
|
// "jsxFactory": "h",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"typeRoots": [
|
||||||
|
"node_modules/@types",
|
||||||
|
"node_modules/@kevisual/types",
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src",
|
||||||
|
"typings.d.ts"
|
||||||
|
]
|
||||||
|
}
|
24
react/vite.config.mjs
Normal file
24
react/vite.config.mjs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { defineConfig } from 'vite';
|
||||||
|
import react from '@vitejs/plugin-react';
|
||||||
|
import dts from 'vite-plugin-dts';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
build: {
|
||||||
|
lib: {
|
||||||
|
entry: './src/index.ts',
|
||||||
|
formats: ['es'],
|
||||||
|
},
|
||||||
|
emptyOutDir: true,
|
||||||
|
sourcemap: true,
|
||||||
|
rollupOptions: {
|
||||||
|
external: ['react', 'react-jsx-runtime', 'zustand'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
react(),
|
||||||
|
dts({
|
||||||
|
insertTypesEntry: true,
|
||||||
|
outputDir: './dist/types',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
21
src/store.ts
21
src/store.ts
@ -20,7 +20,22 @@ export class StoreManager {
|
|||||||
this.stores[key] = createZutandStore(initialStore);
|
this.stores[key] = createZutandStore(initialStore);
|
||||||
return this.stores[key] as StoreApi<T>;
|
return this.stores[key] as StoreApi<T>;
|
||||||
}
|
}
|
||||||
create<T = any, U extends T = any>(initialStore: StateCreator<T, [], [], U>, key: string) {
|
create<T extends Record<string, any>, U extends T = T>(initialStore: StateCreator<T, [], [], U>, key: string) {
|
||||||
|
return this.createStore(initialStore, key) as StoreApi<T>;
|
||||||
|
}
|
||||||
|
createIfNotExists<T extends Record<string, any>, U extends T = T>(
|
||||||
|
initialStore: StateCreator<T, [], [], U>,
|
||||||
|
key: string,
|
||||||
|
opts?: {
|
||||||
|
force?: boolean;
|
||||||
|
},
|
||||||
|
): StoreApi<T> {
|
||||||
|
if (this.stores[key] && !opts?.force) {
|
||||||
|
return this.stores[key];
|
||||||
|
}
|
||||||
|
if (this.stores[key] && opts?.force) {
|
||||||
|
this.removeStore(key);
|
||||||
|
}
|
||||||
return this.createStore(initialStore, key);
|
return this.createStore(initialStore, key);
|
||||||
}
|
}
|
||||||
getStore(key: string) {
|
getStore(key: string) {
|
||||||
@ -84,8 +99,8 @@ export const sub = <T = any>(fn: FnListener<T>, { path, deep, store }: SubOption
|
|||||||
}
|
}
|
||||||
return store.subscribe((newState: T, oldState: T) => {
|
return store.subscribe((newState: T, oldState: T) => {
|
||||||
try {
|
try {
|
||||||
const newPath = get(newState, path);
|
const newPath = get(newState, path as string);
|
||||||
const oldPath = get(oldState, path);
|
const oldPath = get(oldState, path as string);
|
||||||
if (!newPath && !oldPath) {
|
if (!newPath && !oldPath) {
|
||||||
// 都不存在
|
// 都不存在
|
||||||
return;
|
return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user