JSFiddle - React, Tailwind, and code Playground

by junseok choi

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.all.js"></script>
<div class="container">
    <div class="header">
         <h3>Who to follow</h3><a href="#" class="refresh">Refresh</a>
    </div>
    <ul class="suggestions">
        <li class="suggestion1">
            <img />
            <a href="#" target="_blank" class="username">this will not be displayed</a>
            <a href="#" class="close close1">x</a>
        </li>
        <li class="suggestion2">
            <img />
            <a href="#" target="_blank" class="username">neither this</a>
            <a href="#" class="close close2">x</a>
        </li>
        <li class="suggestion3">
            <img />
            <a href="#" target="_blank" class="username">nor this</a>
            <a href="#" class="close close3">x</a>
        </li>
    </ul>
</div>

CSS

body {
    font-family: sans-serif;
    padding: 10px;
}
h3 {
    font-weight: bold;
    display: inline-block;
  padding: 0;
  margin: 0;
}
.refresh {
    font-size: 80%;
    margin-left: 10px;
}
.header {
    background: #ECECEC;
    padding: 5px;
}
.suggestions {
    border: 2px solid #ECECEC;
}
li {
    padding: 5px;
}
li img {
    width: 40px;
    height: 40px;
    border-radius: 20px;
}
li .username, li .close {
    display: inline-block;
    position: relative;
    bottom: 15px;
    left: 5px;
}

JavaScript

// 1. refresh element에 click 이벤트 스트림 생성
var refreshStream = Rx.Observable.fromEvent(document.querySelector(".refresh"), "click");

// 2-1. 1번에서 정의한 스트림에 map을 설정합니다. 이벤트의 스트림마다 해당 이벤트를 실행하겠다고 설정해주는 것 입니다.
// 2-2. just는 초기 데이터가 없기에 trigger로 실행해줍니다.
// 2-3. 두 이벤트 스트림을 merge해서 requestStream을 만듭니다.
// 2-4. 두 이벤트 스트림이 배열로 들어갑니다.
var requestStream = Rx.Observable.just(getUsers()).merge(refreshStream.map(getUsers));

// 3-1. flatMap은 이벤트 스트림을 하나로 합쳐줍니다.
// 3-2. 처음부터 refresh 이벤트가 발생했을리는 없으니 초기에 들어가는 url은 just로 실행해준 값이 될겁니다.
// 3-3. shareReplay는 parameter로 넘겨준 숫자만큼의 item들을 최초 subscribe에서만 처리하여 가지고 있다가, 이후의 subscribe에서는 최초에 가지고 있던 item들을 replay하기 위한 operator입니다.
// 3-4. https://codepen.io/HipsterZipster/pen/aZJeBm
// 3-5. 위 url을 보고 오신다면 responseStream 이벤트가 발생한다면, 가장 마지막 값이 남아있다는 것을 아시게 될겁니다.
var responseStream = requestStream.flatMap( url => {
  console.log("request url : " +  url);
  return Rx.Observable.fromPromise(jQuery.getJSON(url));
}).shareReplay(1);

// 4-1. responseStream 이벤트 발생시 getRandomUser를 실행시킵니다.
// 4-2. 초기값은 없습니다. (초기부터 close 이벤트가 실행되지 않음) / 사실 startWith가 없어도 문제가 생기지 않음
// 4-3. withLastestFrom은 두 개의 이벤트 스트림을 합성하지만, 한쪽 이벤트 스트림의 이벤트가 발생할 때에만 합성을 해줍니다.
// 4-4. 이 메서드를 사용하면, 스트림을 합성할 때 이벤트를 추가할 수 있습니다.
function createLineStream(closeStream) {
  // null, user
  return responseStream.map(getRandomUser)
    .startWith(null)
    .merge(closeStream.withLatestFrom(responseStream, (evt, users) => {
      return getRandomUser(users);
    }));
}

var close1Stream = Rx.Observable.fromEvent(document.querySelector(".close1"), "click");
var close2Stream = Rx.Observable.fromEvent(document.querySelector(".close2"), "click");
var close3Stream = Rx.Observable.fromEvent(document.querySelector(".close3"), "click");

var line1Stream = createLineStream(close1Stream);
var line2Stream = createLineStream(close2Stream);
var line3Stream = createLineStream(close3Stream);

// Run
line1Stream.subscribe(user => {
  renderUser(".suggestion1", user);
});
line2Stream.subscribe(user => {
 ...