init
This commit is contained in:
18
src/clientStore/StoreProvider.tsx
Normal file
18
src/clientStore/StoreProvider.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
'use client';
|
||||
|
||||
import { type PropsWithChildren, useRef } from 'react';
|
||||
import type { StoreInterface, StoreType } from './store';
|
||||
import { initializeStore, Provider } from './store';
|
||||
|
||||
// export interface PreloadedStoreInterface extends Pick<StoreInterface, 'lastUpdate'> {}
|
||||
export interface PreloadedStoreInterface {}
|
||||
|
||||
export default function StoreProvider({ children, ...props }: PropsWithChildren<PreloadedStoreInterface>) {
|
||||
const storeRef = useRef<StoreType>();
|
||||
|
||||
if (!storeRef.current) {
|
||||
storeRef.current = initializeStore(props);
|
||||
}
|
||||
|
||||
return <Provider value={storeRef.current}>{children}</Provider>;
|
||||
}
|
||||
59
src/clientStore/store.ts
Normal file
59
src/clientStore/store.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { createContext, useContext } from 'react';
|
||||
import { createStore, useStore as useZustandStore } from 'zustand';
|
||||
import { PreloadedStoreInterface } from './StoreProvider';
|
||||
|
||||
export interface StoreInterface {
|
||||
lastUpdate: number;
|
||||
light: boolean;
|
||||
count: number;
|
||||
tick: (lastUpdate: number) => void;
|
||||
increment: () => void;
|
||||
decrement: () => void;
|
||||
reset: () => void;
|
||||
}
|
||||
|
||||
function getDefaultInitialState() {
|
||||
return {
|
||||
lastUpdate: new Date(1970, 1, 1).getTime(),
|
||||
light: false,
|
||||
count: 0,
|
||||
} as const;
|
||||
}
|
||||
|
||||
export type StoreType = ReturnType<typeof initializeStore>;
|
||||
|
||||
const storeContext = createContext<StoreType | null>(null);
|
||||
|
||||
export const Provider = storeContext.Provider;
|
||||
|
||||
export function useStore<T>(selector: (state: StoreInterface) => T) {
|
||||
const store = useContext(storeContext);
|
||||
|
||||
if (!store) throw new Error('Store is missing the provider');
|
||||
|
||||
return useZustandStore(store, selector);
|
||||
}
|
||||
|
||||
export function initializeStore(preloadedState: PreloadedStoreInterface) {
|
||||
return createStore<StoreInterface>((set, get) => ({
|
||||
...getDefaultInitialState(),
|
||||
...preloadedState,
|
||||
tick: lastUpdate =>
|
||||
set({
|
||||
lastUpdate,
|
||||
light: !get().light,
|
||||
}),
|
||||
increment: () =>
|
||||
set({
|
||||
count: get().count + 1,
|
||||
}),
|
||||
decrement: () =>
|
||||
set({
|
||||
count: get().count - 1,
|
||||
}),
|
||||
reset: () =>
|
||||
set({
|
||||
count: getDefaultInitialState().count,
|
||||
}),
|
||||
}));
|
||||
}
|
||||
20
src/clientStore/useInterval.ts
Normal file
20
src/clientStore/useInterval.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
'use client';
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
// https://overreacted.io/making-setinterval-declarative-with-react-hooks/
|
||||
export default function useInterval(callback: () => void, delay: number | undefined) {
|
||||
const savedCallback = useRef<typeof callback>();
|
||||
|
||||
useEffect(() => {
|
||||
savedCallback.current = callback;
|
||||
}, [callback]);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = () => savedCallback.current?.();
|
||||
|
||||
if (delay !== null) {
|
||||
const id = setInterval(handler, delay);
|
||||
return () => clearInterval(id);
|
||||
}
|
||||
}, [delay]);
|
||||
}
|
||||
Reference in New Issue
Block a user