Redux 스타일 Store 직접 구현 — state 는 클로저에 가두고, 바꾸는 길은 dispatch → reducer 하나뿐이다.1 Redux 와 달리 이 subscribe 는 등록 즉시 현재 state 로 한 번 부르고, 리스너에 state 를 넘긴다(Redux 리스너는 인자 없이 불리고 getState() 로 읽는다).2
type Reducer<S, A> = (state: S, action: A) => S
type Listener<S> = (state: S) => void
interface Store<S, A> {
getState: () => S
subscribe: (listener: Listener<S>) => () => void
dispatch: (action: A) => A
}
function createStore<S, A>(
reducer: Reducer<S, A>,
preloadedState: S
): Store<S, A> {
let currentState: S = preloadedState
let listeners: Listener<S>[] = []
function getState(): S {
return currentState
}
function subscribe(listener: Listener<S>): () => void {
listener(currentState)
listeners.push(listener)
return function unsubscribe() {
listeners = listeners.filter((l) => l !== listener)
}
}
function dispatch(action: A): A {
currentState = reducer(currentState, action)
listeners.forEach((listener) => {
listener(currentState)
})
return action
}
return {
subscribe,
getState,
dispatch,
}
}
type Item = {
body: string
}
type State = {
items: Item[]
}
type Action = {
type: 'ADD'
payload: Item
}
const store = createStore<State, Action>(
(state, action) => {
switch (action.type) {
case 'ADD':
return {
...state,
items: state.items.concat(action.payload),
}
default:
return state
}
},
{
items: [],
}
)
store.subscribe((state) => {
console.log(state.items)
})
store.dispatch({
type: 'ADD',
payload: {
body: 'hello',
},
})
Footnotes
-
Store | Redux: “The only way to change the state inside it is to dispatch an action on it, which triggers the root reducer function to calculate the new state.” ↩
-
Store | Redux: “It will be called any time an action is dispatched, and some part of the state tree may potentially have changed. You may then call getState() to read the current state tree inside the callback.” ↩