export interface Action<T = any> {
type: T
}
export interface AnyAction extends Action {
// Allows any extra properties to be defined in an action.
[extraProps: string]: any
}
const sampleAciton = {
type: "UNIQUE_TYPE",
payload : "Usually payload is used as a prop but",
iCanAddAnyProp : "technically you can add any prop you want"
}
→ Action은 type을 프로퍼티로 갖고 있고 필요에 따라 아무 property나 추가할 수 있는 단순 오브젝트이다.
export type Reducer<S = any, A extends Action = AnyAction> = (
state: S | undefined,
action: A
) => S
→ Reducer는 State와 Action을 받아서 업데이트 된 State를 반환하는 함수다.
2.2 combineReducers
export type ReducersMapObject<S = any, A extends Action = AnyAction> = {
[K in keyof S]: Reducer<S[K], A>
}
export default function combineReducers<S>(
reducers: ReducersMapObject<S, any>
): Reducer<CombinedState<S>>
export default function combineReducers<S, A extends Action = AnyAction>(
reducers: ReducersMapObject<S, A>
): Reducer<CombinedState<S>, A>
export default function combineReducers<M extends ReducersMapObject>(
reducers: M
): Reducer<
CombinedState<StateFromReducersMapObject<M>>,
ActionFromReducersMapObject<M>
>
export default function combineReducers(reducers: ReducersMapObject) {
const reducerKeys = Object.keys(reducers)
const finalReducers: ReducersMapObject = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
if (process.env.NODE_ENV !== 'production') {
if (typeof reducers[key] === 'undefined') {
warning(`No reducer provided for key "${key}"`)
}
}
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key]
}
}
const finalReducerKeys = Object.keys(finalReducers)
// This is used to make sure we don't warn about the same
// keys multiple times.
let unexpectedKeyCache: { [key: string]: true }
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {}
}
let shapeAssertionError: Error
try {
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertionError = e
}
return function combination(
state: StateFromReducersMapObject<typeof reducers> = {},
action: AnyAction
) {
if (shapeAssertionError) {
throw shapeAssertionError
}
if (process.env.NODE_ENV !== 'production') {
const warningMessage = getUnexpectedStateShapeWarningMessage(
state,
finalReducers,
action,
unexpectedKeyCache
)
if (warningMessage) {
warning(warningMessage)
}
}
let hasChanged = false
const nextState: StateFromReducersMapObject<typeof reducers> = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
const previousStateForKey = state[key]
const nextStateForKey = reducer(previousStateForKey, action)
if (typeof nextStateForKey === 'undefined') {
const actionType = action && action.type
throw new Error(
`When called with an action of type ${
actionType ? `"${String(actionType)}"` : '(unknown type)'
}, the slice reducer for key "${key}" returned undefined. ` +
`To ignore an action, you must explicitly return the previous state. ` +
`If you want this reducer to hold no value, you can return null instead of undefined.`
)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
hasChanged =
hasChanged || finalReducerKeys.length !== Object.keys(state).length
return hasChanged ? nextState : state
}
}
→ CombineReducer는 아래 두 가지 작업을 수행한다.
여러 개의 Reducer를 인자로 받아 하나의 오브젝트로 매핑한다.
state와 action을 인자로 받아 위에서 매핑한 오브젝트를 이용해 state의 특정 부분만 업데이트하는 함수를 반환한다.
2.3 createStore
// createStore params
export default function createStore<
S,
A extends Action,
Ext = {},
StateExt = never
>(
reducer: Reducer<S, A>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
export default function createStore<
S,
A extends Action,
Ext = {},
StateExt = never
>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
export default function createStore<
S,
A extends Action,
Ext = {},
StateExt = never
>(
reducer: Reducer<S, A>,
preloadedState?: PreloadedState<S> | StoreEnhancer<Ext, StateExt>,
enhancer?: StoreEnhancer<Ext, StateExt>
): Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext {
// ...중략
// 내부 변수 선언
let currentReducer = reducer
let currentState = preloadedState as S
let currentListeners: (() => void)[] | null = []
let nextListeners = currentListeners
let isDispatching = false
// ...중략
// dispatch 함수
function dispatch(action: A) {
if (!isPlainObject(action)) {
throw new Error(
`Actions must be plain objects. Instead, the actual type was: '${kindOf(
action
)}'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.`
)
}
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. You may have misspelled an action type string constant.'
)
}
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
}
// ...중략
const store = {
dispatch: dispatch as Dispatch<A>,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
} as unknown as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext
return store
→ createStore는 store 오브젝트를 반환하고 store 오브젝트의 기능은 크게 보면 아래와 같다.
subscribe 함수 제공: state에 업데이트가 일어날 때 이를 subscribe하기 위한 subscribe 함수를 제공한다.
dispatch 함수 제공: state를 업데이트하기 위한 dispatch 함수를 제공한다. dispatch 함수는 reducer를 실행하고(즉 state를 업데이트하고) subscribe 함수를 통해 listener로 등록된 콜백 함수를 순차적으로 실행한다
Redux
1. Flux design pattern
2. Redux
2.1 reducers
→ Action은 type을 프로퍼티로 갖고 있고 필요에 따라 아무 property나 추가할 수 있는 단순 오브젝트이다.
→ Reducer는 State와 Action을 받아서 업데이트 된 State를 반환하는 함수다.
2.2 combineReducers
→ CombineReducer는 아래 두 가지 작업을 수행한다.
2.3 createStore
→ createStore는 store 오브젝트를 반환하고 store 오브젝트의 기능은 크게 보면 아래와 같다.