UNPKG

hooks-for-redux

Version:
224 lines (196 loc) 9.09 kB
/// <reference types="redux" /> /// <reference types="react" /> import { Action, Unsubscribe, AnyAction, Store } from "redux"; import { Component } from "react"; /**************************** util ****************************/ type RestParams<TFunction> = TFunction extends (arg: any, ...args: infer A) => void ? A : never; /**************************** hook ****************************/ /** * use(), a React hook for the Redux Model's state * @param selector optional function to select-from or transform the Redux Model's state. * @param equalityFn optional function to compare two results from your selector; return true if they are different enough to justify trigging a re-render * * `selector` and `equalityFn` work the same as https://react-redux.js.org/api/hooks#useselector. The main difference here is that the selector is optional since * H4R already applies a pre-selector to select your module's "slice" out of the whole redux state. Note, the pre-selector is *always* applied to the entire redux state, and then your optional selector is applied to your module's state. */ interface ReactReduxHookWithOptionalSelector<TState> { <TSelected>( selector: (state: TState) => TSelected, equalityFn?: (left: TSelected, right: TSelected) => boolean ): TSelected; } interface ReactReduxHookWithOptionalSelector<TState> { (): TState; } /**************************** reducers ****************************/ type Reducer<TState> = (state: TState, payload: any) => TState; interface Reducers<TState> { [reducerName: string]: Reducer<TState>; } /**************************** virtualStores ****************************/ type VirtualStoreWithReducers<TState, TReducers> = { getState: () => TState; getReducers: () => TReducers; subscribe: (callback: (state: TState) => void) => Unsubscribe; }; type VirtualStore<TState> = { getState: () => TState; subscribe: (callback: (state: TState) => void) => Unsubscribe; }; /**************************** dispatchers ****************************/ interface PayloadAction<TPayload> extends Action<string> { payload: TPayload; } type SetterDispatcher<TState> = (state: TState) => PayloadAction<TState>; type Dispatcher<TReducer> = (...args: RestParams<TReducer>) => PayloadAction<RestParams<TReducer>[0]>; type Dispatchers<TReducers> = { [K in keyof TReducers]: Dispatcher<TReducers[K]>; }; /**************************** createReduxModule function ****************************/ /** * Defines a top-level property of the redux state including its inital value, all related reducers, and returns a react-hook, dispatchers and virtualStore. * * @param reduxStorePropertyName is the name of the property off the root redux store you are declaring and initializing * * @param initialState is the initial value for your redux state * * @param [reducers] is a map from reducer-name to reducer functions which take the currentState plus optional payload and return a new state. * * @returns a 3-element array: [reactHook, updateDispatcher or dispatcherMap, virtualStore] */ export function createReduxModule<TState>( reduxStorePropertyName: string, initialState: TState ): [ReactReduxHookWithOptionalSelector<TState>, SetterDispatcher<TState>, VirtualStore<TState>]; export function createReduxModule<TState, TReducers extends Reducers<TState>>( reduxStorePropertyName: string, initialState: TState, reducers: TReducers ): [ ReactReduxHookWithOptionalSelector<TState>, Readonly<Dispatchers<TReducers>>, VirtualStoreWithReducers<TState, TReducers> ]; /** * Defines a top-level property of the redux state including its inital value, all related reducers, and returns a react-hook, dispatchers and virtualStore. * * @param reduxStorePropertyName is the name of the property off the root redux store you are declaring and initializing * * @param initialState is the initial value for your redux state * * @param [reducers] is a map from reducer-name to reducer functions which take the currentState plus optional payload and return a new state. * * @returns a 3-element array: [reactHook, updateDispatcher or dispatcherMap, virtualStore] * * @deprecated Use createReduxModule instead. Same API, new name. */ export function useRedux<TState>( reduxStorePropertyName: string, initialState: TState ): [ReactReduxHookWithOptionalSelector<TState>, SetterDispatcher<TState>, VirtualStore<TState>]; export function useRedux<TState, TReducers extends Reducers<TState>>( reduxStorePropertyName: string, initialState: TState, reducers: TReducers ): [ ReactReduxHookWithOptionalSelector<TState>, Readonly<Dispatchers<TReducers>>, VirtualStoreWithReducers<TState, TReducers> ]; /**************************** other hooks-for-redux functions ****************************/ export interface ReduxStoreWithInjectReducers extends Store { injectReducer(key: string, reducer: Reducer<object>): void; } /** * Auto-vivifies a store if setStore has not been called. Otherwise, it returns the store passed to setStore. * * @returns current or newly crated store */ export function getStore(): ReduxStoreWithInjectReducers; /** * Call setStore to provide your own store for hooks-for-redux to use. You'll need to use this if you want to use middleware. * * @param [store] store to set in the store-registry * * @returns store */ export function setStore(store: ReduxStoreWithInjectReducers): ReduxStoreWithInjectReducers; /** * Creates a Redux store that holds the state tree. * The only way to change the data in the store is to call `dispatch()` on it. * * There should only be a single store in your app. To specify how different * parts of the state tree respond to actions, you may combine several * reducers * into a single reducer function by using `combineReducers`. * * @template TState State object type. * * @param reducers An object whose values correspond to different reducer * functions that need to be combined into one. * * @param [initialState] The initial state. * * @param [enhancer] The store enhancer. You may optionally specify it to * enhance the store with third-party capabilities such as middleware, time * travel, persistence, etc. The only store enhancer that ships with Redux * is `applyMiddleware()`. * * @returns A Redux store that lets you read the state, dispatch actions and * subscribe to changes. */ export function createStore<TState>( reducers: Reducers<object>, initialState?: TState, enhancer?: any ): ReduxStoreWithInjectReducers; export interface ProviderProps<A extends Action = AnyAction> { /** * The single Redux store in your application. */ store?: Store<any, A>; /** * Optional context to be used internally in react-redux. Use React.createContext() to create a context to be used. * If this is used, generate own connect HOC by using connectAdvanced, supplying the same context provided to the * Provider. Initial value doesn't matter, as it is overwritten with the internal state of Provider. */ context?: Context<ReactReduxContextValue>; } /** * Makes the Redux store available to the connect() calls in the component hierarchy below. */ export class Provider<A extends Action = AnyAction> extends Component<ProviderProps<A>> {} /**************************** type testing ****************************/ // type Unsubscribe = () => void // export interface Action<T = any> { // type: T // } // const [getBar2, setBar2] = useRedux('bar', 0) // getBar2() // setBar2(1) // const [getBar, {addSevenToBar, addToBar, addString}] = useRedux('bar', 0, { // addSevenToBar: (state) => state + 7, // addToBar: (state, payload) => state + payload, // addString:(state, myThing:string) => state + +myThing // }) // getBar().toPrecision() // addSevenToBar() // addToBar(8) // addString("9") // TEST: https://www.typescriptlang.org/play/index.html#code/C4TwDgpgBAggxsAlgewHYB4AqA+KBeKAb1EgC4pMBfAWACgToBVVAZwFcAjFuAJ0Q+gEAFAEp8uAG7JEAEzp0GUAEoQWwAAoBDHpoC2LLLgKYoEAB7AIqGSyhDtAc3KbUIADRQAdN8ctyiVAAzCB5YMTxJaRkoAH5YKHJUCAkQ+XpwaABxCGBLHkN8O3DcTDSAvMDNOGgtEAAbZE0ZeCQ0AvNLa1sWlAw1PlQHXEI6KCgwTXrGmXJS2hpaBQyoAGUcvIARRBYJ4DgACxCC4TVNS1niqFqGpp62nDTFFRk2avycQqFT84oPCamms5XJc5ktIMoIC83gZMB5GEYIXBkDwZOgANYQEDIQJQRgeZ6vI44bCPZZbHZnA5EglvBFCbyeXzkFRqLQ6fRYGkhbDFUZXSY3ZoIXroFkabR6GFcnjYADaAAYALokxbpcHk3ZUnhSyGE7UIka0MaygDSUACUAxWJxmGlLEV5A1lMO7ztpuVdAWYOgADVEDxgGxNHUVsBkRAAOqIYD7O1YUNnCAeW266EGvkOHIJn6icQUbNJjM5O3kXMRCh2tx89hcXj8CCluDBuocKpo0vfBuYAuXKSyS7MGvcPgCT2k8F+gNBkNhngQePARPpo1QTPAAulkEFqsrod1gSN5utuDtuyd0jdxeWXtRAesTjD+tj1UyCBwOraaCBNioYVoKBsCwEAEmYC5LkIsjkP0AQOB4ATRogwYbvmV4QCI5Cyny2S5ESBbYDuYxrDhPBOnsLpgZY+F8pOgZIbO86XkudCKnQr7vp+UDfr+rSoABQEgRRSYVqmIS2B0Vg2BCUKiYJyZ2tg2BCHyYyQVA0GDARYzmqgCF0YmszbspUBztJ2qzJWdDoVAmErmM2F5IJVG2RCTRoHUIDoKRWo6qZLAKZpYw0dOobhlGMZxoxlhySJ+o7ixqpIqwwDWWuABC2gAEweEBwDpTwGWKoUgHAbqZhCAA5K2PDlR48oiHQaWZaIdA5XlGVCAAjPVCVoGoKU5HlHiEFATQyJgyCDSNMgyGsKSoONeVQJQhUEMVIEVVVNVQPKQ1GaNs1WAt2gdqhlydlAADUUAAOwBVNY0TcdZ6oX8ArTIkbC6AIPBnahl3jG9TQ7pQYgAPSg1AHhpI1PCiJ4YbqHOcDbL0zWqvtySHY9sPdaNR2wwAHN1QA