UNPKG

@zubridge/electron

Version:

A streamlined state management library for Electron applications using Zustand.

96 lines (95 loc) 3.76 kB
import { useStore } from 'zustand'; import { createStore as createZustandStore } from 'zustand/vanilla'; // Store registry to implement singleton pattern // Maps handler objects to their corresponding stores const storeRegistry = new WeakMap(); // Internal implementation of store creation const createStore = (bridge) => { // Check if a store already exists for these handlers if (storeRegistry.has(bridge)) { return storeRegistry.get(bridge); } // Create a new store if one doesn't exist const newStore = createZustandStore((setState) => { // subscribe to changes bridge.subscribe((state) => setState(state)); // get initial state bridge.getState().then((state) => setState(state)); // no state keys - they will all come from main return {}; }); // Register the store storeRegistry.set(bridge, newStore); return newStore; }; // Create Electron-specific handlers export const createHandlers = () => { if (typeof window === 'undefined' || !window.zubridge) { throw new Error('Zubridge handlers not found in window. Make sure the preload script is properly set up.'); } return window.zubridge; }; /** * Creates a hook for accessing the store state in React components */ export const createUseStore = (customHandlers) => { const handlers = customHandlers || createHandlers(); const vanillaStore = createStore(handlers); const useBoundStore = (selector) => useStore(vanillaStore, selector); Object.assign(useBoundStore, vanillaStore); // return store hook return useBoundStore; }; /** * Creates a dispatch function for sending actions to the main process * * @template S The state type * @template TActions A record of action types to payload types mapping (optional) * @param customHandlers Optional custom handlers to use instead of window.zubridge * @returns A typed dispatch function * * @example * // Basic usage * const dispatch = useDispatch(); * * @example * // With typed actions * type CounterActions = { * 'COUNTER:INCREMENT': void; * 'COUNTER:DECREMENT': void; * 'COUNTER:SET': number; * }; * const dispatch = useDispatch<State, CounterActions>(); * dispatch({ type: 'COUNTER:SET', payload: 5 }); // Type-safe payload * dispatch({ type: 'UNKNOWN' }); // Type error */ export const useDispatch = (customHandlers) => { const handlers = customHandlers || createHandlers(); // Ensure we have a store for these handlers const store = storeRegistry.has(handlers) ? storeRegistry.get(handlers) : createStore(handlers); // Create a dispatch function that will handle both generic and typed actions const dispatch = ((action, payload) => { if (typeof action === 'function') { // Handle thunks - execute them with the store's getState and our dispatch function return action(store.getState, dispatch); } // Handle string action type with payload if (typeof action === 'string') { // Only pass the payload parameter if it's not undefined, and handle promise return value return payload !== undefined ? handlers.dispatch(action, payload) : handlers.dispatch(action); } // For action objects, normalize to standard Action format if (typeof action.type !== 'string') { throw new Error(`Invalid action type: ${action.type}. Expected a string.`); } const normalizedAction = { type: action.type, payload: action.payload, }; // Return the promise from dispatch return handlers.dispatch(normalizedAction); }); return dispatch; }; // Export environment utilities export * from './utils/environment';