store init
This commit is contained in:
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',
|
||||
}),
|
||||
],
|
||||
});
|
||||
Reference in New Issue
Block a user