Skip to content

Instantly share code, notes, and snippets.

@rolandcoops
Last active October 22, 2019 09:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rolandcoops/f6b63905e6afbb8193ca2835b947a86c to your computer and use it in GitHub Desktop.
Save rolandcoops/f6b63905e6afbb8193ca2835b947a86c to your computer and use it in GitHub Desktop.
Simple TypeScript implementation of Redux
/**
* Types & Interfaces
*/
type State = { [key: string]: any }
type Reducer = (currentState: State, action: Action) => State;
type Listener = (currentState?: State) => void;
interface ReducerTree {
[propName: string]: Reducer
}
interface Action {
type: string,
[propName: string]: any // allow any additional props
}
interface Store {
getState (): State
subscribe (listener: Function): () => void
dispatch (action: Action): void
}
/**
* Store creation
*/
export function createStore (rootReducer: Reducer, currentState: State = {}): Store {
let subscriptions: Listener[] = []
const getState = (): State => currentState
function subscribe (listener: Listener) {
subscriptions.push(listener)
return function unsubscribe () {
subscriptions = subscriptions.filter((subscriber) => subscriber !== listener)
}
}
function dispatch (action: Action) {
currentState = rootReducer(currentState, action)
subscriptions.forEach((listener: Listener): void => {
listener(currentState)
})
}
// set up default store
dispatch({ type: 'INIT' })
return { getState, subscribe, dispatch }
}
/**
* Helpers
*/
export function combineReducers (reducers: ReducerTree): Reducer {
const entries = Object.entries(reducers)
return (prevState: State, action: Action): State =>
entries.reduce((nextState: State, [key, reducer]: [string, Reducer]) => {
nextState[key] = reducer(prevState[key], action)
return nextState
}, {})
}
/**
* Example
*/
const initialCounterState = {
count: 0,
}
const counterReducer = (state: State = initialCounterState, action: Action) => {
switch (action.type) {
case 'INCREMENT_COUNTER':
return { ...state, count: state.count + 1 }
case 'DECREMENT_COUNTER':
return { ...state, count: state.count - 1 }
default:
return state
}
}
const initialUserState = {
name: '',
}
const userReducer = (state: State = initialUserState, action: Action) => {
switch (action.type) {
case 'CHANGE_NAME':
return { ...state, name: action.name }
default:
return state
}
}
const reducers = {
counter: counterReducer,
user: userReducer,
}
const store = createStore(combineReducers(reducers))
store.subscribe(console.log)
store.dispatch({ type: 'INCREMENT_COUNTER' })
store.dispatch({ type: 'CHANGE_NAME', name: 'Roland' })
store.dispatch({ type: 'DECREMENT_COUNTER' })
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment