티스토리 뷰

React 

: props변화에따른 saga와 componentWillReceiveProps()에 대한 생각 및 활용 


| 시작하기에 앞서,


일단 componentWillReceiveProps는 리액트 주기중, props에 변화가 있을때, 그 props를 catch하여 이전 props와 변화한 props를 비교등을 통해, 리액트 웹앱의 세부작업을 할수있는 주기이다. 


사내 사이트를 만들던 중, 카트에 자동으로 메뉴가 하나 추가하는 기능을 구현했어야 했는데, 

그 케이스가 아래의 두가지였다.


1. 처음사이트에 들어왔을때 유저 ux를 위해 카트에 메뉴를 하나추가

2.예약기능을 위해 날짜를 변경했을시 다시 카트를 clear하고 메뉴를 하나 추가



| 어려움을 겪었던 부분


기존에 1. 처음사이트에 들어왔을때 유저 ux를 위해 카트에 메뉴를 하나추가 기능은 이미 구현이 되어있었고, 예약기능이 추가되었기에 2.예약기능을 위해 날짜를 변경했을시 다시 카트를 clear하고 메뉴를 하나 추가의 case가 추가되었었는데, 아래가 기존의 1. 처음사이트에 들어왔을때 유저 ux를 위해 카트에 메뉴를 하나추가를 위한 redux saga 함수부분이다. 


> 1. 처음사이트에 들어왔을때 유저 ux를 위해 카트에 메뉴를 하나추가해줬던 부분

export function* getOrderAmount(actions) {
 const { params } = actions;
 const { dailies, cart } = yield select();
 const orderAmount = yield call(getAmount, params);
 yield put(getOrderAmountSuccess(orderAmount));
  맨마지막에 cart가 비었으면 1추가
  if (Object.keys(cart).length === 0) {
    yield put(clearCart());
    yield put(addCart({ menuId: dailies.dailies.data[0].menuIdx, amount: 1 }));
}
}

getOrderAmount에 cart가 비었으면 addCart 액션생성함에 액션을 dispatch하는 식으로 구현하였는데, 그이유는 cart에 추가되는 액션은 제일 마지막에 불려야 한다고 생각했고, redux dev tool로 확인한 결과 getOrderAmount(날짜,메뉴,회사 주소별 주문수를 트래킹하는 api에 호출하여 그 개수를 return해줌)부분이 제일마지막에 불리고있는 부분이었고 따라서 그 부분에 아래와 같은 cart state에 아무것도 없는것을 확인해 addcart를 해주는 식으로 해주었었다.

if (Object.keys(cart).length === 0) {
    yield put(clearCart());
    yield put(addCart({ menuId: dailies.dailies.data[0].menuIdx, amount: 1 }));
}


> 2.예약기능을 위해 날짜를 변경했을시 다시 카트를 clear하고 메뉴를 하나 추가하는 부분


2.예약기능을 위해 날짜를 변경했을시 다시 카트를 clear하고 메뉴를 하나 추가하는 부분은 아래와 같이, componentWillReceiveProps 주기안에 this.props.dailies.data[0].menuIdx !== dailies.data[0].menuIdx 과 같이 불러온 메뉴가 다를시 (날짜에 따라 menu가 바뀌므로) cart에 add해주는식으로 구현했었다.

      componentWillReceiveProps(nextProps) {
       ...
       // 예약시간 변경될때, 맨처음 메뉴를불러올때 cart clear 해주고 카트 다시추가
       if (
        (this.props.dailies.data &&
           this.props.dailies.data[0] &&
           this.props.dailies.data[0].menuIdx !== dailies.data[0].menuIdx)
      ) {
         this.props.clearCart();
         this.props.addCart({
           menuId: dailies.data[0].menuIdx,
           amount: 1,
        });
      }
    }
그런데 문제가 생겼었던 부분은 redux saga에서는 바뀌고있는 props에 대한 handling이 안되기 때문에, 변화한 props와 이전 props 대해서 비교를 해주고 하는 부분을 넣어줄수 없어서, 1.기능의 addCart 부분과 2.기능의 addcart 부분이 서로 충돌이 났는지, 이전날짜의 menu가 카트에 들어가있는등의 오류가 발생했다. 이는 cart에 추가되는 기능을 getOrderAmount라는 다른 기능을 하고있는 함수에  넣어준것도 잘못된 것이었고, 같은기능을 하는(addCart) 액션생성함수에 디스패치 하는 부분을 각 case마다 분리해 넣어주다 보니 워낙 사이트에서 redux state도 많고, 액션생성함수에 dispatch하는것이 많다보니 서로 순서가 뒤죽박죽 되어 제대로된 기능구현이 되지 못하고 있던것이었다.


| 해결한 방법

따라서 위의 1과 2의 case를 하나로 합치고 그부분을 componentWillRecieveProps 주기에서만 handling하도록 코드를 수정해주었다.
즉, 아래에서와 같이 getOrderAmount에서 1의 case인 cart가 비었는지 확인해 카트에 메뉴를 추가해서 넣어주던 부분을 없애고,
export function* getOrderAmount(actions) {
 const { params } = actions;
 const { dailies, cart } = yield select();
 const orderAmount = yield call(getAmount, params);
 yield put(getOrderAmountSuccess(orderAmount));
 // 맨마지막에 cart가 비었으면 1추가
 // if (Object.keys(cart).length === 0) {
 //   yield put(clearCart());
 //   yield put(addCart({ menuId: dailies.dailies.data[0].menuIdx, amount: 1 }));
 // }
}

아래와 같이, componentWillReceiveProps 주기에서 addCart하는식으로 구현해주었다.결국 addCart 액션생성함수는 api호출로 받은 여러 props에 변화에 따라서 카트에 추가해주는 그 case가 두가지로 나뉘는 것에 따른 가능 구현이므로 componentWillReceiveProps 주기에서 아래와 같이 if에 case들을 묶어 처리해주는 방식으로 처리해주는게 더 옳은것이었다.

if (
    (this.props.dailies.data &&
       this.props.dailies.data[0] &&
       this.props.dailies.data[0].menuIdx !== dailies.data[0].menuIdx) ||
    (Object.keys(cart).length === 0 &&
       this.props.meta.timeSlot !== meta.timeSlot)
  )

위는 case에 따른 if 조건들이고,
1. 처음사이트에 들어왔을때 유저 ux를 위해 카트에 메뉴를 하나추가  Object.keys(cart).length === 0 &&  this.props.meta.timeSlot !== meta.timeSlot)
2.예약기능을 위해 날짜를 변경했을시 (메뉴가변경됨) 다시 카트를 clear하고 메뉴를 하나 추가 this.props.dailies.data && this.props.dailies.data[0] &&  this.props.dailies.data[0].menuIdx !== dailies.data[0].menuIdx
아래는 전체 함수부분이다.


componentWillRecieveProps(nextProps){
// 예약시간 변경될때, 맨처음 메뉴를불러올때 cart clear 해주고 카트 다시추가
   if (
    (this.props.dailies.data &&
       this.props.dailies.data[0] &&
       this.props.dailies.data[0].menuIdx !== dailies.data[0].menuIdx) ||
    (Object.keys(cart).length === 0 &&
       this.props.meta.timeSlot !== meta.timeSlot)
  ) {
     this.props.clearCart();
     this.props.addCart({
       menuId: dailies.data[0].menuIdx,
       amount: 1,
    });
  }
}

| 느낀점
saga 함수에서는 yield 로 동기적인 순서로 기능을 수행하긴 하지만, props에 변화에따른 기능구현은 cover가 어려우므로, saga함수에서는 그런 props변화에따라 기능을 구현해야하는것은 고려할 필요가 없는것같다.
또한 한 saga 함수에서 여러가지 기능을 수행하게 하면 안되며, api call이나, 다른 액션 디스패치에 뒤따르는 다른액션을 디스패치해주는 등의 확장된 역할을 수행해주면 되는것이고, 이전 props와 바뀐 props 변화에 따른 (redux state가 props로 매칭되므로 redux state의 변화도 포함한다.) 특정 기능을 수행할때는componentWillReceiveProps(nextProps) 에서 그 기능을 구현해줘야 하는것이었다.



댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함