diff --git a/src/GlobalState.tsx b/src/GlobalState.tsx new file mode 100644 index 0000000..310cb36 --- /dev/null +++ b/src/GlobalState.tsx @@ -0,0 +1,184 @@ +import React, {PropsWithChildren, useCallback, useContext, useState} from "react"; + +/** + * Global state modifiers functions. + */ +export class GlobalStateReducers +{ + /** + * The current state (readonly). + * @protected + */ + protected state: Readonly; + /** + * State update system. + * @private + */ + private stateUpdater: React.Dispatch; + + /** + * Assign changes to the current state. + * @param stateUpdate Changes to apply to the current state. + */ + setState(stateUpdate: Partial) + { + // Apply update object to the current state. + this.state = Object.assign({}, this.state, stateUpdate); + // Update the changed state. + this.stateUpdater(this.state); + } +} + +/** + * Reducers definition interface for easy type definition. + */ +interface ReducerHack +{ + /** + * The current state (readonly). + */ + state: Readonly; + /** + * State update system. + */ + stateUpdater: React.Dispatch; +} + +/** + * Main global state class. + */ +export class GlobalState> +{ + /** + * Global state context. + * @protected + */ + protected context: React.Context; + /** + * State update system, used in the reducers class. + * @protected + */ + protected stateUpdater: React.Dispatch; + + /** + * Create a new global state. + * @param defaultValue The default value of the global state. + * @param reducers Reducers class. + */ + constructor(protected defaultValue: S, protected reducers: R) + { + // Create a new context for the global state. + this.context = React.createContext(defaultValue); + } + + /** + * Get the context for the global state. + */ + getContext(): React.Context + { return this.context; } + + /** + * Get the default value of the global state. + */ + getDefaultValue(): S + { return this.defaultValue; } + + /** + * Assign the state update system, used in the reducers class. + * @param updater State update system instance. + */ + setStateUpdater(updater: React.Dispatch): void + { + this.stateUpdater = updater; + } + + /** + * Get the configured state reducers class for the current state. + * @param state The state on which the reducers should apply. + */ + getStateReducers(state: S): R + { + // Assign the given state. + (this.reducers as unknown as ReducerHack).state = state; + // Assign the current state updater. + (this.reducers as unknown as ReducerHack).stateUpdater = this.stateUpdater; + return this.reducers; + } +} + +/** + * Global state Provider hook. + * @param globalState Global state for which to get the global state Provider. + */ +export function useGlobalStateProvider>(globalState: GlobalState): React.FunctionComponent> +{ + // Get the provider from the given global state context. + const Provider = globalState.getContext().Provider; + + // Create the global state provider component. + return useCallback((props: PropsWithChildren<{}>) => { + // Get the current state of the global state. + const [state, setState] = useState(globalState.getDefaultValue()); + // Pass the state update system to the global state. + globalState.setStateUpdater(setState); + + // Use the context provider and render children. + return ( + + {props.children} + + ); + }, [Provider]); +} + +/** + * Global state Provider component + * @constructor + */ +export function GlobalStateProvider>({globalState, children}: React.PropsWithChildren<{ + /** + * Global state for which to get the global state Provider. + */ + globalState: GlobalState; +}>) +{ + // Get the current global state provider. + const Provider = useGlobalStateProvider(globalState); + + return ( + // Render the children inside the provider. + {children} + ); +} + +/** + * Get the global state and its reducers. + * @param globalState The global state to get. + */ +export function useGlobalState>(globalState: GlobalState): [S, R] +{ + // Get the global state data (from its context state). + const ctx = useContext(globalState.getContext()); + // Return the context state (global state data), and the global state reducers for this current state. + return [ctx, globalState.getStateReducers(ctx)]; +} + +/** + * Get the reducers of the global state. + * @param globalState The global state for which to get the reducers. + */ +export function useGlobalStateReducers>(globalState: GlobalState): R +{ + // Return the global state reducers for the current global state. + return globalState.getStateReducers(useContext(globalState.getContext())); +} + +/** + * Get the global state data. + * @param globalState The global state for which to get the data. + */ +export function useGlobalStateValue>(globalState: GlobalState): S +{ + // Return the global state data (from its context state). + return useContext(globalState.getContext()); +}