Created
October 14, 2025 22:14
-
-
Save antoniopresto/875c4435e8f31a2c85f87d805ffac6fa to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * A plain object that represents an intention to change the state. | |
| * Actions are the only way to get data into the store. | |
| */ | |
| export interface Action<T = any> { | |
| type: T; | |
| } | |
| /** | |
| * An Action type which accepts any other properties. | |
| */ | |
| export interface AnyAction extends Action { | |
| [extraProps: string]: any; | |
| } | |
| /** | |
| * A function that takes the current state and an action, and returns the next state. | |
| * @template S The type of state maintained by the store. | |
| * @template A The type of action which can be dispatched. | |
| */ | |
| export type Reducer<S = any, A extends Action = AnyAction> = ( | |
| state: S | undefined, | |
| action: A | |
| ) => S; | |
| /** | |
| * A function to dispatch an action. It is the only way to trigger a state change. | |
| */ | |
| export type Dispatch<A extends Action = AnyAction> = (action: A) => A; | |
| /** | |
| * A function that is called when the state in the store changes. | |
| */ | |
| export type Unsubscribe = () => void; | |
| /** | |
| * The single source of truth for your application's state. | |
| * @template S The type of state maintained by the store. | |
| * @template A The type of action which can be dispatched. | |
| */ | |
| export interface Store<S = any, A extends Action = AnyAction> { | |
| dispatch: Dispatch<A>; | |
| getState(): S; | |
| subscribe(listener: () => void): Unsubscribe; | |
| replaceReducer(nextReducer: Reducer<S, A>): void; | |
| } | |
| /** | |
| * A store creator is a function that creates a Redux store. | |
| */ | |
| export type StoreCreator = <S, A extends Action>( | |
| reducer: Reducer<S, A>, | |
| preloadedState?: S | |
| ) => Store<S, A>; | |
| /** | |
| * A store enhancer is a higher-order function that composes a store creator to return a new, enhanced store creator. | |
| */ | |
| export type StoreEnhancer<Ext = {}, StateExt = {}> = ( | |
| next: StoreCreator | |
| ) => StoreCreator; | |
| /** | |
| * A middleware is a higher-order function that composes a dispatch function to return a new dispatch function. | |
| */ | |
| export interface MiddlewareAPI<D extends Dispatch = Dispatch, S = any> { | |
| dispatch: D; | |
| getState(): S; | |
| } | |
| export interface Middleware< | |
| DispatchExt = {}, | |
| S = any, | |
| D extends Dispatch = Dispatch | |
| > { | |
| (api: MiddlewareAPI<D, S>): ( | |
| next: Dispatch<AnyAction> | |
| ) => (action: AnyAction) => any; | |
| } | |
| /** | |
| * A utility function to check if an object is a plain object. | |
| */ | |
| function isPlainObject(obj: any): boolean { | |
| if (typeof obj !== 'object' || obj === null) return false; | |
| let proto = obj; | |
| while (Object.getPrototypeOf(proto) !== null) { | |
| proto = Object.getPrototypeOf(proto); | |
| } | |
| return Object.getPrototypeOf(obj) === proto; | |
| } | |
| /** | |
| * Creates a Redux store that holds the complete state tree of your app. | |
| * There should only be a single store in your app. | |
| * | |
| * @param reducer A function that returns the next state tree, given the current state tree and an action to handle. | |
| * @param preloadedState The initial state. | |
| * @param enhancer The store enhancer. | |
| * @returns A Redux store that lets you read the state, dispatch actions and subscribe to changes. | |
| */ | |
| export function createStore<S, A extends Action, Ext, StateExt>( | |
| reducer: Reducer<S, A>, | |
| preloadedState?: S, | |
| enhancer?: StoreEnhancer<Ext, StateExt> | |
| ): Store<S, A> & Ext { | |
| if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { | |
| enhancer = preloadedState as StoreEnhancer<Ext, StateExt>; | |
| preloadedState = undefined; | |
| } | |
| if (typeof enhancer !== 'undefined') { | |
| if (typeof enhancer !== 'function') { | |
| throw new Error('Expected the enhancer to be a function.'); | |
| } | |
| return enhancer(createStore)(reducer, preloadedState) as Store<S, A> & Ext; | |
| } | |
| if (typeof reducer !== 'function') { | |
| throw new Error('Expected the reducer to be a function.'); | |
| } | |
| let currentReducer = reducer; | |
| let currentState = preloadedState as S; | |
| let currentListeners: (() => void)[] | null = []; | |
| let nextListeners = currentListeners; | |
| let isDispatching = false; | |
| function ensureCanMutateNextListeners() { | |
| if (nextListeners === currentListeners) { | |
| nextListeners = currentListeners.slice(); | |
| } | |
| } | |
| function getState(): S { | |
| if (isDispatching) { | |
| throw new Error( | |
| 'You may not call store.getState() while the reducer is executing.' | |
| ); | |
| } | |
| return currentState; | |
| } | |
| function subscribe(listener: () => void): Unsubscribe { | |
| if (typeof listener !== 'function') { | |
| throw new Error('Expected the listener to be a function.'); | |
| } | |
| if (isDispatching) { | |
| throw new Error( | |
| 'You may not call store.subscribe() while the reducer is executing.' | |
| ); | |
| } | |
| let isSubscribed = true; | |
| ensureCanMutateNextListeners(); | |
| nextListeners.push(listener); | |
| return function unsubscribe() { | |
| if (!isSubscribed) { | |
| return; | |
| } | |
| if (isDispatching) { | |
| throw new Error( | |
| 'You may not unsubscribe from a store listener while the reducer is executing.' | |
| ); | |
| } | |
| isSubscribed = false; | |
| ensureCanMutateNextListeners(); | |
| const index = nextListeners.indexOf(listener); | |
| nextListeners.splice(index, 1); | |
| currentListeners = null; | |
| }; | |
| } | |
| function dispatch(action: A): A { | |
| if (!isPlainObject(action)) { | |
| throw new Error('Actions must be plain objects.'); | |
| } | |
| if (typeof action.type === 'undefined') { | |
| throw new Error('Actions may not have an undefined "type" property.'); | |
| } | |
| if (isDispatching) { | |
| throw new Error('Reducers may not dispatch actions.'); | |
| } | |
| try { | |
| isDispatching = true; | |
| currentState = currentReducer(currentState, action); | |
| } finally { | |
| isDispatching = false; | |
| } | |
| const listeners = (currentListeners = nextListeners); | |
| for (let i = 0; i < listeners.length; i++) { | |
| const listener = listeners[i]; | |
| listener(); | |
| } | |
| return action; | |
| } | |
| function replaceReducer(nextReducer: Reducer<S, A>): void { | |
| if (typeof nextReducer !== 'function') { | |
| throw new Error('Expected the nextReducer to be a function.'); | |
| } | |
| currentReducer = nextReducer; | |
| dispatch({ type: '@@redux/REPLACE' } as A); | |
| } | |
| dispatch({ type: '@@redux/INIT' } as A); | |
| return { | |
| dispatch, | |
| subscribe, | |
| getState, | |
| replaceReducer, | |
| } as Store<S, A> & Ext; | |
| } | |
| /** | |
| * Composes single-argument functions from right to left. The rightmost | |
| * function can take multiple arguments; the remaining functions must be unary. | |
| */ | |
| export function compose(...funcs: Function[]): Function { | |
| if (funcs.length === 0) { | |
| return (arg: any) => arg; | |
| } | |
| if (funcs.length === 1) { | |
| return funcs[0]; | |
| } | |
| return funcs.reduce((a, b) => (...args: any[]) => a(b(...args))); | |
| } | |
| /** | |
| * Creates a store enhancer that applies middleware to the dispatch method | |
| * of the Redux store. This is handy for a variety of tasks, such as expressing | |
| * asynchronous actions in a concise manner, or logging every action payload. | |
| */ | |
| export function applyMiddleware( | |
| ...middlewares: Middleware[] | |
| ): StoreEnhancer { | |
| return (createStore: StoreCreator) => <S, A extends Action>( | |
| reducer: Reducer<S, A>, | |
| preloadedState?: S | |
| ) => { | |
| const store = createStore(reducer, preloadedState); | |
| let dispatch: Dispatch = () => { | |
| throw new Error( | |
| 'Dispatching while constructing your middleware is not allowed.' | |
| ); | |
| }; | |
| const middlewareAPI: MiddlewareAPI<Dispatch<A>, S> = { | |
| getState: store.getState, | |
| dispatch: (action: A) => dispatch(action), | |
| }; | |
| const chain = middlewares.map(middleware => middleware(middlewareAPI)); | |
| dispatch = compose(...chain)(store.dispatch); | |
| return { | |
| ...store, | |
| dispatch, | |
| }; | |
| }; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment