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…
Reference in a new issue