Created
July 14, 2019 01:56
-
-
Save velopert/d418517581e580c5191f385cf8f2f3b0 to your computer and use it in GitHub Desktop.
React Context + useReducer + async actions example
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
// 이 함수는 파라미터로 액션의 타입 (예: GET_USER) 과 Promise 를 만들어주는 함수를 받아옵니다. | |
export function createAsyncDispatcher(type, promiseFn) { | |
// 성공, 실패에 대한 액션 타입 문자열을 준비합니다. | |
const SUCCESS = `${type}_SUCCESS`; | |
const ERROR = `${type}_ERROR`; | |
// 새로운 함수를 만듭니다. | |
// ...rest 를 사용하여 나머지 파라미터를 rest 배열에 담습니다. | |
async function actionHandler(dispatch, ...rest) { | |
dispatch({ type }); // 요청 시작됨 | |
try { | |
const data = await promiseFn(...rest); // rest 배열을 spread 로 넣어줍니다. | |
dispatch({ | |
type: SUCCESS, | |
data | |
}); // 성공함 | |
} catch (e) { | |
dispatch({ | |
type: ERROR, | |
error: e | |
}); // 실패함 | |
} | |
} | |
return actionHandler; // 만든 함수를 반환합니다. | |
} | |
export const initialAsyncState = { | |
loading: false, | |
data: null, | |
error: null | |
}; | |
// 로딩중일 때 바뀔 상태 객체 | |
const loadingState = { | |
loading: true, | |
data: null, | |
error: null | |
}; | |
// 성공했을 때의 상태 만들어주는 함수 | |
const success = data => ({ | |
loading: false, | |
data, | |
error: null | |
}); | |
// 실패했을 때의 상태 만들어주는 함수 | |
const error = error => ({ | |
loading: false, | |
data: null, | |
error: error | |
}); | |
// 세가지 액션을 처리하는 리듀서를 만들어줍니다 | |
// type 은 액션 타입, key 는 리듀서서 사용할 필드 이름입니다 (예: user, users) | |
export function createAsyncHandler(type, key) { | |
// 성공, 실패에 대한 액션 타입 문자열을 준비합니다. | |
const SUCCESS = `${type}_SUCCESS`; | |
const ERROR = `${type}_ERROR`; | |
// 함수를 새로 만들어서 | |
function handler(state, action) { | |
switch (action.type) { | |
case type: | |
return { | |
...state, | |
[key]: loadingState | |
}; | |
case SUCCESS: | |
return { | |
...state, | |
[key]: success(action.data) | |
}; | |
case ERROR: | |
return { | |
...state, | |
[key]: error(action.error) | |
}; | |
default: | |
return state; | |
} | |
} | |
// 반환합니다 | |
return handler; | |
} |
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 React, { createContext, useReducer, useContext } from 'react'; | |
import { | |
createAsyncDispatcher, | |
createAsyncHandler, | |
initialAsyncState | |
} from './asyncActionUtils'; | |
import * as api from './api'; // api 파일에서 내보낸 모든 함수들을 불러옴 | |
// UsersContext 에서 사용 할 기본 상태 | |
const initialState = { | |
users: initialAsyncState, | |
user: initialAsyncState | |
}; | |
const usersHandler = createAsyncHandler('GET_USERS', 'users'); | |
const userHandler = createAsyncHandler('GET_USER', 'user'); | |
// 위에서 만든 객체 / 유틸 함수들을 사용하여 리듀서 작성 | |
function usersReducer(state, action) { | |
switch (action.type) { | |
case 'GET_USERS': | |
case 'GET_USERS_SUCCESS': | |
case 'GET_USERS_ERROR': | |
return usersHandler(state, action); | |
case 'GET_USER': | |
case 'GET_USER_SUCCESS': | |
case 'GET_USER_ERROR': | |
return userHandler(state, action); | |
default: | |
throw new Error(`Unhanded action type: ${action.type}`); | |
} | |
} | |
// State 용 Context 와 Dispatch 용 Context 따로 만들어주기 | |
const UsersStateContext = createContext(null); | |
const UsersDispatchContext = createContext(null); | |
// 위에서 선언한 두가지 Context 들의 Provider 로 감싸주는 컴포넌트 | |
export function UsersProvider({ children }) { | |
const [state, dispatch] = useReducer(usersReducer, initialState); | |
return ( | |
<UsersStateContext.Provider value={state}> | |
<UsersDispatchContext.Provider value={dispatch}> | |
{children} | |
</UsersDispatchContext.Provider> | |
</UsersStateContext.Provider> | |
); | |
} | |
// State 를 쉽게 조회 할 수 있게 해주는 커스텀 Hook | |
export function useUsersState() { | |
const state = useContext(UsersStateContext); | |
if (!state) { | |
throw new Error('Cannot find UsersProvider'); | |
} | |
return state; | |
} | |
// Dispatch 를 쉽게 사용 할 수 있게 해주는 커스텀 Hook | |
export function useUsersDispatch() { | |
const dispatch = useContext(UsersDispatchContext); | |
if (!dispatch) { | |
throw new Error('Cannot find UsersProvider'); | |
} | |
return dispatch; | |
} | |
export const getUsers = createAsyncDispatcher('GET_USERS', api.getUsers); | |
export const getUser = createAsyncDispatcher('GET_USER', api.getUser); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment