2017-02-16 7 views
2

나는이 흐름 서사시 만드는 방법을 알아 내려고, redux-observable와 사투를 벌인거야 통해 데이터를 장식하는 REDUX-관찰 서사시를 사용하여 :여러 작업

  1. 을 보내 GET_ITEMS_REQUEST 작업을 수신을 HTTP 요청은 일부 항목을 해당 항목의 ID를
  2. 잡아 얻을
  3. DETA와 첫 번째 요청에서 항목을 장식하는 항목에 대한 좀 더 자세한 내용을 얻기 위해 또 다른 HTTP 요청을 전송하는 GET_ITEM_DETAILS_REQUEST 조치를 파견하기 마지막 요청을 보낸 후 GET_ITEMS_SUCCESS 액션을 보내면 redux 상태가 업데이트됩니다.

3 단계에서 4 단계로 이동하면 문제가 발생합니다. 아이템 ID를 가지고 GET_ITEM_DETAILS_REQUEST을 파견하는 방법을 알고 있지만 아이템 세부 정보 응답을 얻기 위해 GET_ITEM_DETAILS_REQUEST 액션을 청취/구독하는 법을 모르겠습니다. 당신이 항목을받은 후

function getItemsEpic(action$) { 

    return action$ 
    // step 1 
    .ofType('GET_ITEMS_REQUEST') 
    .mergeMap(() => { 
     // step 2 
     return Observable.from(Api.getItems()) 
    }) 
    .mergeMap((items) => { 
     // step 3 
     const itemIds = items.map((item) => item.id); 
     return Observable.of({ 
     type: 'GET_ITEM_DETAILS_REQUEST', 
     ids: itemIds 
     }); 
    }) 
    // ... what now? 
    .catch(() => { 
     return Observable.of({ 
     type: 'GET_ITEMS_FAILURE' 
     }); 
    }); 
} 

답변

1

한 가지 방법이다, GET_ITEM_DETAILS_FULFILLED을 대기 시작하고 즉시 startWith()를 사용하여 GET_ITEM_DETAILS_REQUEST 킥오프 :

지금까지, 나는 다음 있습니다. 다른 서사시가 세부 정보를 보게되고 GET_ITEM_DETAILS_FULFILLED을 내고 다른 서사시가 참을성있게 기다리고 있으며 두 개 (항목 + 세부 정보)를 함께 압축합니다.

const getItemDetailsEpic = action$ => 
    action$ 
    .ofType('GET_ITEM_DETAILS_REQUEST') 
    .mergeMap(({ ids }) => 
     Observable.from(Api.getItemDetails(ids)) 
     .map(details => ({ 
      type: 'GET_ITEM_DETAILS_FULFILLED', 
      details 
     })) 
    ); 

const getItemsEpic = action$ => 
    action$ 
    .ofType('GET_ITEMS_REQUEST') 
    .mergeMap(() => 
     Observable.from(Api.getItems()) 
     .mergeMap(items => 
      action$.ofType('GET_ITEM_DETAILS_FULFILLED') 
      .take(1) // don't listen forever! IMPORTANT! 
      .map(({ details }) => ({ 
       type: 'GET_ITEMS_SUCCESS', 
       items: items.map((item, i) => ({ 
       ...item, 
       detail: details[i] 
       // or the more "safe" `details.find(detail => detail.id === item.id)` 
       // if your data structure allows. Might not be necessary if the 
       // order is guaranteed to be the same 
       })) 
      })) 
      .startWith({ 
       type: 'GET_ITEM_DETAILS_REQUEST', 
       ids: items.map(item => item.id) 
      }) 
     ) 
    ); 

별도로, 나는 당신이 외부 관찰 가능한 체인에 catch()을 넣어 나타났습니다. 이것은 아마도 당신이 원하는 것을 완전히하지 않을 것입니다. 오류가 상위 체인에 도달하면 전체 서사가 종료되며 더 이상 미래를 청취하지 않습니다 GET_ITEMS_REQUEST! 이것은 매우 중요한 차이점이며 우리는 종종 "관찰 가능한 체인을 격리"라고 부릅니다. 오류가 예상보다 많이 전파되는 것을 원하지는 않습니다.

// GOOD 
const somethingEpic = action$ => 
    action$.ofType('SOMETHING') 
    .mergeMap(() => 
     somethingThatMayFail() 
     .catch(e => Observable.of({ 
      type: 'STUFF_BROKE_YO', 
      payload: e, 
      error: true 
     })) 
    ); 

// NOT THE SAME THING! 
const somethingEpic = action$ => 
    action$.ofType('SOMETHING') 
    .mergeMap(() => 
     somethingThatMayFail() 
    ) 
    .catch(e => Observable.of({ 
     type: 'STUFF_BROKE_YO', 
     payload: e, 
     error: true 
    })); 

외부 체인에서 캐치를 원할 때가 있지만 일반적으로 복구 할 수없는 오류에 대해서만 마지막으로 문제가됩니다.

+0

나는 중요한 변화를 만들었다. 나는'action $ .ofType ('GET_ITEM_DETAILS_FULFILLED')'뒤에'take (1)'을 추가하는 것을 잊었다. 서사시가 영원히 듣기 때문에 'mergeMap'이 무기한 축적 될 것이기 때문에 이것은 중요합니다. – jayphelps

+0

감사합니다. 'GET_ITEM_DETAILS_FULFILLED'가 즉각적으로 전송 된 상세 요청에 대한 응답이 될 것을 보장하는 것은 무엇입니까? 세부 정보 요청이 응답으로 돌아 오기 전에 또 다른 'GET_ITEM_DETAILS_FULFILLED'이 (가) 발생하면 어떻게 될까요? – Hristo

+0

덧붙여서,'catch()'의 주석에 감사드립니다. 나는 그것으로 또한 고투하고있다. – Hristo