9일차(7.5~7.6)

yoon Y·2021년 12월 29일

05. redux-thunk로 프로미스 다루기

// thunk함수 
// dispatch함수 실행 시 인자로 넣어서 사용한다
// dispatch(getPosts()) - 반환값이 함수 - thunk가 실행시켜서 반환값을 다음 미들웨어에 넘김

export const getPosts = () => async dispatch => {
  dispatch({ type: GET_POSTS }); // 요청이 시작됨 - 상태 바꾸는 action
  try {
    const posts = await postsAPI.getPosts(); // API 호출
    dispatch({ type: GET_POSTS_SUCCESS, posts }); // 성공 - 상태 바꾸는 action
  } catch (e) {
    dispatch({ type: GET_POSTS_ERROR, error: e }); // 실패 - 상태 바꾸는 action
  }
};

리덕스 모듈 리팩토링하기

// src/lib/asyncUtils.js

                                   //기본 액션이름  //api함수
export const createPromiseThunk = (type, promiseCreator) => {
  const [SUCCESS, ERROR] = [`${type}_SUCCESS`, `${type}_ERROR`];

  return param => async dispatch => {
    // 요청 시작
    dispatch({ type, param });
    try {
      const payload = await promiseCreator(param);
      dispatch({ type: SUCCESS, payload }); 
    } catch (e) {
      dispatch({ type: ERROR, payload: e, error: true }); 
    }
  };
};

// 파라미터를 넣어서 원하는 상태의 객체를 반환하도록
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
  })
};

export const handleAsyncActions = (type, key) => {
  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;
    }
  };
};

적용

// action의 기본 이름과 api함수만 넣으면 된다
export const getPosts = createPromiseThunk(GET_POSTS, postsAPI.getPosts);
export const getPost = createPromiseThunk(GET_POST, postsAPI.getPostById);

// 반환되는 객체를 함수를 실행해 사용할 수 있다
const initialState = {
  posts: reducerUtils.initial(),
  post: reducerUtils.initial()
};

export default function posts(state = initialState, action) {
  switch (action.type) {
    case GET_POSTS:
    case GET_POSTS_SUCCESS:
    case GET_POSTS_ERROR:
      return handleAsyncActions(GET_POSTS, 'posts')(state, action);
    case GET_POST:
    case GET_POST_SUCCESS:
    case GET_POST_ERROR:
      return handleAsyncActions(GET_POST, 'post')(state, action);
    default:
      return state;
  }
}

라우트 설정 시 문제점

  1. 특정 포스트를 연 다음 뒤로 갔을 때, 포스트 목록을 또 다시 불러오게 되면서 로딩중...이 나타난다
  2. 특정 포스트를 읽고, 뒤로 간 다음에 다른 포스트를 열면 이전에 열었던 포스트가 잠깐 보여졌다가 로딩중... 이 보여진다

06. API 재로딩 문제 해결하기

포스트 목록 재로딩 문제 해결하기

  1. 데이터가 이미 존재한다면 요청을 하지 않도록 하는 방법 - 최신 데이터가 아니다
// 최초 한 번 실행하고 data가 없을 때마다 요청
useEffect(() => {
    if (data) return;
    dispatch(getPosts());
  }, [data, dispatch]);
  1. 로딩을 새로하긴 하는데, 로딩중... 을 띄우지 않는 것

    사용자에게 좋은 경험을 제공하면서도 뒤로가기를 통해 다시 포스트 목록을 조회 할 때 최신 데이터를 보여줄 수 있다

// 받아온 데이터가 있다면 loading을 띄우지 않고 전 데이터 유지 후 새로 받아옴
export const handleAsyncActions = (type, key, keepData = false) => {
  const [SUCCESS, ERROR] = [`${type}_SUCCESS`, `${type}_ERROR`];
  return (state, action) => {
    switch (action.type) {
      case type:
        return {
          ...state,
          [key]: reducerUtils.loading(keepData ? state[key].data : null)
        };
      case SUCCESS:
        return {
          ...state,
          [key]: reducerUtils.success(action.payload)
        };
      case ERROR:
        return {
          ...state,
          [key]: reducerUtils.error(action.error)
        };
      default:
        return state;
    }
  };
};

출처 https://react.vlpt.us/

profile
#프론트엔드

0개의 댓글