이전 글에 작성한 thunk 코드원리와 흐름 설명하면서 리팩토링을 좀 함
그렇기 때문에 우선 미들웨어를 담당할 JS 하나 생성 (async.js라고 작성함 작성명 마음대로)
📖 코드가 흘러가는 순서
이대로 코드 리팩토링
👀 겹치거나 거의 비슷한 변수,함수 탐색
1.initialState
2. api get하는 함수
3. 리듀서 case
1.initialState
const initialState = { posts: { loading: false, data: null, error: null }, post: { loading: false, data: null, error: null } };
전체를 조회하는 getPosts와 특정 id를 찾는 getPost 두개의 함수를 실행 해야하기 때문에 initialState를 두개 쓰지만 이름만 다르지 값이 똑같기 때문에 범용적인 하나로 바꿔야함.
✍ 리팩토링으로 합치기
export const reducerUtils = {
//초기상태와 각 액션의 상태들을 담아놓고 상황에 맞게 바꿀 수 있는 객체를 만들어 넣기
initial: (initialData = null) => ({
loading: false,
data: initialData,
error: null
}),
}
loading: (prevState = null) => ({
loading: true,
data: prevState,
error: null
}),
//로딩상태
success: payload => ({
loading: false,
data: payload,
error: null
}),
//성공상태
error: error => ({
loading: false,
data: null,
error: error
})
//실패상태
//최종 initialState
const initialState = {
postsData: reducerUtils.initial(),
findPostData: reducerUtils.initial()
};
2. API GET하는 함수
export const getPosts = () => async dispatch => {
dispatch({ type: GET_POSTS }); // 요청이 시작됨
try {
const posts = await postsAPI.getPosts(); // API 호출
dispatch({ type: GET_POSTS_SUCCESS, posts }); // 성공
} catch (e) {
dispatch({ type: GET_POSTS_ERROR, error: e }); // 실패
}
};
// thunk 함수에서도 파라미터를 받아와서 사용 할 수 있습니다.
export const getPost = id => async dispatch => {
dispatch({ type: GET_POST }); // 요청이 시작됨
try {
const post = await postsAPI.getPostById(id); // API 호출
dispatch({ type: GET_POST_SUCCESS, post }); // 성공
} catch (e) {
dispatch({ type: GET_POST_ERROR, error: e }); // 실패
}
};
강의보면 그냥 둬도 될 것 같은데 굳이 합친단다..
export const getAllPostRequest = createPromiseThunk(GET_POSTS, postsAPI.getPosts);
export const getPostRequest = createPromiseThunk(GET_POST, postsAPI.getPostById);
변수를 만들어서 = 미들웨어(타입,액션할함수) 이렇게 넣어주면 됨
CreatePromiseThunk (thunk middleware)
export const createPromiseThunk = (type, promiseCreator) => {
const [SUCCESS, ERROR] = [`${type}_SUCCESS`, `${type}_ERROR`];
//type = GET_POSTS 또는 GET_POST
//promiseCreator은 api/post에 있는 getPosts나 getPostById 함수를 가져옴
const thunkCreator = (param) => async (dispatch) => {
dispatch({ type });
// dispatch({ type }); = GET_POSTS 또는 GET_POST 요청이 시작됨
try {
const payload = await promiseCreator(param);
//여기서 param은 결과값
dispatch({
type: SUCCESS,
payload,
});
} catch (e) {
dispatch({
type: ERROR,
payload: e,
error: true,
});
}
};
return thunkCreator;
};
액션을 디스패치 하게되면 미들웨어가 작동 후 1단계 리듀서에 toss
1단계 posts 리듀서
export default function posts(state = initialState, action) {
switch (action.type) {
case GET_POSTS:
case GET_POSTS_SUCCESS:
case GET_POSTS_ERROR:
return getALLPostReducer(state, action);
case GET_POST:
case GET_POST_SUCCESS:
case GET_POST_ERROR:
return getPostReducer(state, action);
default:
return state;
}
}
액션이 발생하면 posts 리듀서에서 조회대상을 파단하여 다음 리듀서에 데이터를 보냄
const getALLPostReducer = handleAsyncActions(GET_POSTS, "postsData");
const getPostReducer = handleAsyncActions(GET_POST, "findPostData");
//전체를 조회하는 함수와 특정개체를 찾는 함수생성 및 initialState지정
2단계 handleAsyncActions 리듀서
export const handleAsyncActions = (type, key) => {
//type = 조회대상 , key = 어느 initialState인지
const [SUCCESS, ERROR] = [`${type}_SUCCESS`, `${type}_ERROR`];
return (state, action) => {
switch (action.type) {
case type:
return {
...state,
[key]: reducerUtils.loading(),
};
case SUCCESS:
return {
...state,
[key]: reducerUtils.success(action.payload),
};
case ERROR:
return {
...state,
[key]: reducerUtils.error(action.payload),
};
default:
return state;
}
};
};
끝