Add global state system.
This commit is contained in:
		
							parent
							
								
									6a331024a5
								
							
						
					
					
						commit
						6d3aafb15c
					
				
					 1 changed files with 184 additions and 0 deletions
				
			
		
							
								
								
									
										184
									
								
								src/GlobalState.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								src/GlobalState.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,184 @@
 | 
			
		|||
import React, {PropsWithChildren, useCallback, useContext, useState} from "react";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Global state modifiers functions.
 | 
			
		||||
 */
 | 
			
		||||
export class GlobalStateReducers<S>
 | 
			
		||||
{
 | 
			
		||||
	/**
 | 
			
		||||
	 * The current state (readonly).
 | 
			
		||||
	 * @protected
 | 
			
		||||
	 */
 | 
			
		||||
	protected state: Readonly<S>;
 | 
			
		||||
	/**
 | 
			
		||||
	 * State update system.
 | 
			
		||||
	 * @private
 | 
			
		||||
	 */
 | 
			
		||||
	private stateUpdater: React.Dispatch<S>;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Assign changes to the current state.
 | 
			
		||||
	 * @param stateUpdate Changes to apply to the current state.
 | 
			
		||||
	 */
 | 
			
		||||
	setState(stateUpdate: Partial<S>)
 | 
			
		||||
	{
 | 
			
		||||
		// 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<S>
 | 
			
		||||
{
 | 
			
		||||
	/**
 | 
			
		||||
	 * The current state (readonly).
 | 
			
		||||
	 */
 | 
			
		||||
	state: Readonly<S>;
 | 
			
		||||
	/**
 | 
			
		||||
	 * State update system.
 | 
			
		||||
	 */
 | 
			
		||||
	stateUpdater: React.Dispatch<S>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Main global state class.
 | 
			
		||||
 */
 | 
			
		||||
export class GlobalState<S, R extends GlobalStateReducers<S>>
 | 
			
		||||
{
 | 
			
		||||
	/**
 | 
			
		||||
	 * Global state context.
 | 
			
		||||
	 * @protected
 | 
			
		||||
	 */
 | 
			
		||||
	protected context: React.Context<S>;
 | 
			
		||||
	/**
 | 
			
		||||
	 * State update system, used in the reducers class.
 | 
			
		||||
	 * @protected
 | 
			
		||||
	 */
 | 
			
		||||
	protected stateUpdater: React.Dispatch<S>;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 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<S>(defaultValue);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Get the context for the global state.
 | 
			
		||||
	 */
 | 
			
		||||
	getContext(): React.Context<S>
 | 
			
		||||
	{ 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<S>): 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<S>).state = state;
 | 
			
		||||
		// Assign the current state updater.
 | 
			
		||||
		(this.reducers as unknown as ReducerHack<S>).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<S, R extends GlobalStateReducers<S>>(globalState: GlobalState<S, R>): React.FunctionComponent<PropsWithChildren<{}>>
 | 
			
		||||
{
 | 
			
		||||
	// 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 (
 | 
			
		||||
			<Provider value={state}>
 | 
			
		||||
				{props.children}
 | 
			
		||||
			</Provider>
 | 
			
		||||
		);
 | 
			
		||||
	}, [Provider]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Global state Provider component
 | 
			
		||||
 * @constructor
 | 
			
		||||
 */
 | 
			
		||||
export function GlobalStateProvider<S, R extends GlobalStateReducers<S>>({globalState, children}: React.PropsWithChildren<{
 | 
			
		||||
	/**
 | 
			
		||||
	 * Global state for which to get the global state Provider.
 | 
			
		||||
	 */
 | 
			
		||||
	globalState: GlobalState<S, R>;
 | 
			
		||||
}>)
 | 
			
		||||
{
 | 
			
		||||
	// Get the current global state provider.
 | 
			
		||||
	const Provider = useGlobalStateProvider(globalState);
 | 
			
		||||
 | 
			
		||||
	return (
 | 
			
		||||
		// Render the children inside the provider.
 | 
			
		||||
		<Provider>{children}</Provider>
 | 
			
		||||
	);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get the global state and its reducers.
 | 
			
		||||
 * @param globalState The global state to get.
 | 
			
		||||
 */
 | 
			
		||||
export function useGlobalState<S, R extends GlobalStateReducers<S>>(globalState: GlobalState<S, R>): [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<S, R extends GlobalStateReducers<S>>(globalState: GlobalState<S, R>): 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<S, R extends GlobalStateReducers<S>>(globalState: GlobalState<S, R>): S
 | 
			
		||||
{
 | 
			
		||||
	// Return the global state data (from its context state).
 | 
			
		||||
	return useContext(globalState.getContext());
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue