store init
This commit is contained in:
parent
b5dde4e823
commit
152fda350e
25
package.json
25
package.json
@ -8,12 +8,14 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "rollup -c -w",
|
||||
"dev:lib": "rollup -c -w",
|
||||
"build": "npm run clean && rollup -c",
|
||||
"build:app": "npm run build && rsync dist/* ../deploy/dist",
|
||||
"clean": "rm -rf dist"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
"dist",
|
||||
"react/dist"
|
||||
],
|
||||
"keywords": [
|
||||
"kevisual",
|
||||
@ -23,20 +25,21 @@
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^28.0.1",
|
||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||
"@rollup/plugin-typescript": "^12.1.1",
|
||||
"@rollup/plugin-commonjs": "^28.0.3",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-typescript": "^12.1.2",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"immer": "^10.1.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"nanoid": "^5.0.9",
|
||||
"rollup": "^4.27.4",
|
||||
"rollup-plugin-dts": "^6.1.1",
|
||||
"nanoid": "^5.1.5",
|
||||
"rollup": "^4.37.0",
|
||||
"rollup-plugin-dts": "^6.2.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.7.2",
|
||||
"zustand": "^5.0.1"
|
||||
"tsup": "^8.4.0",
|
||||
"typescript": "^5.8.2",
|
||||
"zustand": "^5.0.3"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@ -65,6 +68,10 @@
|
||||
"./web": {
|
||||
"import": "./dist/web.js",
|
||||
"require": "./dist/web.js"
|
||||
},
|
||||
"./react": {
|
||||
"import": "./react/dist/store-react.js",
|
||||
"types": "./react/dist/index.d.ts"
|
||||
}
|
||||
},
|
||||
"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);
|
||||
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);
|
||||
}
|
||||
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) => {
|
||||
try {
|
||||
const newPath = get(newState, path);
|
||||
const oldPath = get(oldState, path);
|
||||
const newPath = get(newState, path as string);
|
||||
const oldPath = get(oldState, path as string);
|
||||
if (!newPath && !oldPath) {
|
||||
// 都不存在
|
||||
return;
|
||||
|
Loading…
x
Reference in New Issue
Block a user