Created
April 3, 2020 01:09
-
-
Save DominicTobias-b1/9490b1a76336170021f1f67fcf4c26ca to your computer and use it in GitHub Desktop.
Multi-store Redux with hooks
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
// limitOrderStore.ts | |
import { ReduxStore } from 'src/global' | |
import { LimitOrderActionTypes, LimitOrderActions, LimitOrderState } from './interfaces' | |
const defaultLimitOrderState: LimitOrderState = { | |
baseAmount: '', | |
quotePrice: '', | |
} | |
function reducer(state: LimitOrderState, action: LimitOrderActions): LimitOrderState { | |
switch (action.type) { | |
case LimitOrderActionTypes.SET_BASE_AMOUNT: | |
return { | |
...state, | |
baseAmount: action.payload, | |
} | |
case LimitOrderActionTypes.SET_QUOTE_PRICE: | |
return { | |
...state, | |
quotePrice: action.payload, | |
} | |
default: | |
throw new Error(`Action does not exist: ${JSON.stringify(action)}`) | |
} | |
} | |
export const limitOrderStore = new ReduxStore(reducer, defaultLimitOrderState) | |
// limitOrderActions.ts | |
import { LimitOrderActionTypes, LimitOrderActions } from './interfaces' | |
export function setBaseAmount(value: string): LimitOrderActions { | |
return { | |
type: LimitOrderActionTypes.SET_BASE_AMOUNT, | |
payload: value, | |
} | |
} | |
export function setQuotePrice(value: string): LimitOrderActions { | |
return { | |
type: LimitOrderActionTypes.SET_QUOTE_PRICE, | |
payload: value, | |
} | |
} | |
// interfaces.ts | |
export interface LimitOrderState { | |
baseAmount: string | |
quotePrice: string | |
} | |
export enum LimitOrderActionTypes { | |
SET_BASE_AMOUNT = 'setBaseAmount', | |
SET_QUOTE_PRICE = 'setQuotePrice', | |
} | |
interface SetLimitOrderBaseAmountAction { | |
type: LimitOrderActionTypes.SET_BASE_AMOUNT | |
payload: string | |
} | |
interface SetLimitOrderQuotePriceAction { | |
type: LimitOrderActionTypes.SET_QUOTE_PRICE | |
payload: string | |
} | |
export type LimitOrderActions = SetLimitOrderBaseAmountAction | SetLimitOrderQuotePriceAction |
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
interface ActionShape { | |
type: string | |
payload: unknown | |
} | |
interface DevTools { | |
connect: () => void | |
send: (action: string, payload: unknown) => void | |
} | |
export class ReduxStore<TState, TActions extends ActionShape> { | |
public state: TState | |
private listeners: Listener<TState>[] | |
private reducer: Reducer<TState, TActions> | |
private devTools: DevTools | |
constructor( | |
reducer: Reducer<TState, TActions>, | |
initialState: TState = {} as TState | |
) { | |
this.reducer = reducer | |
this.listeners = [] | |
this.state = initialState | |
this.devTools = typeof window !== 'undefined' && window?.__REDUX_DEVTOOLS_EXTENSION__?.connect() | |
} | |
listen(listener: Listener<TState>): void { | |
this.listeners.push(listener) | |
} | |
unlisten(listener: Listener<TState>): void { | |
this.listeners = this.listeners.filter(l => l !== listener) | |
} | |
dispatch = (action: TActions): void => { | |
const nextState = this.reducer(this.state, action) | |
if (nextState !== this.state) { | |
this.state = nextState | |
this.listeners.forEach(l => l(nextState)) | |
if (this.devTools) { | |
this.devTools.send(action.type, action.payload) | |
} | |
} | |
} | |
} | |
export type Dispatch<TAction> = (action: TAction) => void | |
export type Listener<TState> = (nextState: TState) => void | |
export type Reducer<TState, TAction> = (state: TState, action: TAction) => TState |
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
import { useState, useRef, useEffect } from 'react' | |
import { ReduxStore } from '../ReduxStore' | |
interface ActionShape { | |
type: string | |
payload: unknown | |
} | |
export function useStoreSelector<TState, TActions extends ActionShape, ReducedTState>( | |
globalStore: ReduxStore<TState, TActions>, | |
stateGetter: (state: TState) => ReducedTState | |
): ReducedTState { | |
const [state, setState] = useState(stateGetter(globalStore.state)) | |
// We don't want to re-create the listener as we want to unlisten on unmount | |
// of the component which uses this hook, so we "tunnel" the state in. | |
const stateRef = useRef(state) | |
stateRef.current = state | |
const listener = useRef((nextState: TState) => { | |
const stateUpdate = stateGetter(nextState) | |
if (stateRef.current !== stateUpdate) { | |
setState(stateUpdate) | |
} | |
}) | |
useEffect(() => { | |
globalStore.listen(listener.current) | |
const currentListener = listener.current | |
return () => globalStore.unlisten(currentListener) | |
}, [globalStore]) | |
return state | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment