Skip to content

Instantly share code, notes, and snippets.

@kyungmi
Last active November 18, 2018 10:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kyungmi/7c77e21e52877c0dff6f7a465b9123cd to your computer and use it in GitHub Desktop.
Save kyungmi/7c77e21e52877c0dff6f7a465b9123cd to your computer and use it in GitHub Desktop.
RxJS 퀵스타트 8장
const { fromEvent } = rxjs;
const { switchMap, takeUntil } = rxjs.operators;
const view = document.getElementById('carousel');
const start$ = fromEvent(view, 'mousedown');
const move$ = fromEvent(view, 'mousemove');
const end$ = fromEvent(view, 'mouseup');
const drag$ = start$.pipe(
// Observable을 평탄하게 합치기 위해 mergeMap을 사용할 수 있으나
// start$에서 데이터가 발생할 때마다 move$가 생성되므로 기존 데이터를 자동으로 종료하기 위해 switchMap을 사용한다.
switchMap(start => move$.pipe(
// takeUntil에 전달된 end$ Observable에서 데이터가 발생하는 순간 대상의 상태를 종료 처리하고 자동으로 구독을 해제한다.
takeUntil(end$),
)),
);
drag$.subscribe(e => console.log(e));
// MouseEvent {isTrusted: true, screenX: 1074, screenY: 551, clientX: 163, clientY: 106, …}
// MouseEvent {isTrusted: true, screenX: 1101, screenY: 548, clientX: 190, clientY: 104, …}
// MouseEvent {isTrusted: true, screenX: 1130, screenY: 548, clientX: 219, clientY: 104, …}
// MouseEvent {isTrusted: true, screenX: 1163, screenY: 548, clientX: 252, clientY: 104, …}
const { fromEvent } = rxjs;
const { switchMap, takeUntil, map } = rxjs.operators;
const view = document.getElementById('carousel');
// Observable을 받아 Observable을 반환하는 오퍼레이터를 만듬
const toPos = (obs$) => obs$.pipe(map(e => e.pageX));
const start$ = fromEvent(view, 'mousedown').pipe(toPos);
const move$ = fromEvent(view, 'mousemove').pipe(toPos);
const end$ = fromEvent(view, 'mouseup').pipe(toPos);
const drag$ = start$.pipe(
switchMap(startX => move$.pipe(
// 처음 X 좌표에서 이동한 거리를 반환
map(moveX => moveX - startX),
takeUntil(end$),
)),
);
drag$.subscribe(v => console.log(v));
// 1
// 2
// 5
// 10
// 15
// 25
/**
* drag$ Observable에서 데이터가 발생한 순간부터 end$ Observable의 데이터가 **딱 한번** 발생한 경우가 drop$이다.
*/
const { take, first } = rxjs.operators;
const drop$ = drag$.pipe(
// 기존 Observable을 자동 종료하기 위해서 switchMap 사용
// switchMap 대신 mergeMap을 사용하면 Observer에서 동일 데이터를 drag$ observable 이벤트 개수만큼 발생시킨다.
switchMap(dragX => end$.pipe(take(1))),
// switchMap(dragX => end$.pipe(first())),
);
drop$.subscribe(v => console.log(v));
// 341

Best Practice (239p)

  • 직접적으로 구독을 해제(unsubscribe)하지 않는다. 가급적이면 takeUntil, take, first 오퍼레이터를 사용해 자동으로 구독을 해제하도록 한다.

operators

  • take: end$에 take operator를 적용하여 end$의 데이터가 딱 한 번 발생할때 end$ 데이터 발생을 중지시킨다.
  • first: 인자가 없는 경우 take(1)과 같지만, 첫 번째 인자로 조건 함수를 전달받을 수 있어서 데이터를 조건별로 분류할 수 있다.
const click$ = fromEvent(document, 'click').pipe(
  first(ev => ev.target.tagName === 'DIV'),
);

operators

  • startWith

  • withLatestFrom

    • http://rxmarbles.com/#withLatestFrom
    • A라는 Observable 데이터가 발생했을 시 B, C, ...Observable의 최신 데이터를 함께 전달받을 수 있게 해준다.
    • withLatestFrom은 전달한 Observable에서 데이터가 발생하지 않으면 원래 Observable도 반환하지 않는다.
    • 마지막 인자로 함수를 전달하지 않으면 배열로 값들이 전달된다.
const { fromEvent } = rxjs;
const { map, withLatestFrom, switchMap, startWith } = rxjs.operators;
const viewWidth$ = fromEvent(window, 'resize').pipe(
// 구독시에 초기값을 전달 (필수)
// `withLatestFrom`은 전달받은 Observable에서 데이터가 발생하지 않으면 원래 Observable도 반환하지 않는다.
startWith(0),
map(() => view.clientWidth),
);
const drop$ = drag$.pipe(
switchMap(dragX => end$.pipe(first())),
// drop$ 이벤트가 발생하는 시점에 size$의 최신 값을 전달받음
withLatestFrom(viewWidth$),
);
drop$.subscribe(([drogX, viewWidth]) => console.log([drogX, viewWidth]));
// (2) [110, 865]
const { fromEvent } = rxjs;
const { map } = rxjs.operators;
const viewWidth$ = fromEvent(window, 'resize').pipe(
map(() => view.clientWidth),
);
viewWidth$.subscribe(width => console.log('view의 넓이', width);
  • drag$ Observable은 Cold Observable은 완전히 독립적인 영역을 갖고 있어 구독하는 Observer마다 별개의 데이터를 전달한다.
  • 데이터는 drag일때도, drop일때도 중복 발생한다.
  • drag$를 공유자원으로 만들면 drag$, drop$이 각각 한 번씩 발생한다.
  • 음? 두 번씩 안 발생하는데❓
const { fromEvent } = rxjs;
const { switchMap, takeUntil, map } = rxjs.operators;
const view = document.getElementById('carousel');
// Observable을 받아 Observable을 반환하는 오퍼레이터를 만듬
const toPos = (obs$) => obs$.pipe(map(e => e.pageX));
const start$ = fromEvent(view, 'mousedown').pipe(toPos);
const move$ = fromEvent(view, 'mousemove').pipe(toPos);
const end$ = fromEvent(view, 'mouseup').pipe(toPos);
const drag$ = start$.pipe(
switchMap(startX => move$.pipe(
// 처음 X 좌표에서 이동한 거리를 반환
map(moveX => moveX - startX),
takeUntil(end$),
)),
share(),
);
const drop$ = drag$.pipe(
switchMap(dragX => end$.pipe(first())),
);
drop$.subscribe(v => console.log(v));
const { merge } = rxjs;
const carousel$ = merge(drag$, drop$);
carousel$.subscribe(v => console.log('캐러셀 데이터', v);

operators

  • merge: 두 개 이상의 Observable을 합친다.

참고

import { paint } from './canvas.js';
const { fromEvent } = Rx.Observable;
const { skipUntil, takeUntil } = Rx.operators;

const move$ = fromEvent(document, 'mousemove');
const down$ = fromEvent(document, 'mousedown')
const up$ = fromEvent(document, 'mouseup')

const paints$ = move$.pipe(
  skipUntil(down$),
  takeUntil(up$)
);

paints$.subscribe(paint);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment