Help us understand the problem. What is going on with this article?

recomposeとRxJSでpropsをstream化する方法

More than 1 year has passed since last update.

長年recomposeでのObservable利用というのが理解出来なかったが、一部唐突に理解できたので忘れる前にメモ。

前提の考え:propsだけをstreamとして考える

とにかくRxを考える上で自分の理解を妨げていたのが「Everything is stream」という考え方だった。

redux-observableを覚えた際は、streamとなるのがReduxのActionだけであると限定出来たから理解できたと考えている。

そのため今回は mapPropsStream の使い方のみフォーカスする。そのためstreamになるのはコンポーネントに渡され続けるpropsだけだ。
(それ以外のものは未だに使い方がいまいちわかってない所がある。特に componentFromStreamはいまいち用途を想像出来てない。)

例:文字数カウンター

例えばこんな文字数カウンターを考える

    export class TextArea extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          text: ""
        }
      }
      render() {
        return (
          <div>
            <textarea
              value={this.state.text}
              onChange={(e) => {
                this.setState({ text: e.target.value })
              }}
            />
            <TextCounter text={this.state.text} />
          </div>
        )
      }
    }

TextCounterの実装は一番愚直に考えればこんな感じだろう

    const TextCounter = ({ text }) => {
      const count = text.count
      return (
        <div>
          <div>
            {count}
            文字
          </div>
          <div>{text}</div>
        </div>
      )
    }

ここで文字入力の度に更新が走ることを危惧した場合、class化してメモ化するなど様々手段は考えられるが、今回ここでpropsをObservableにする方法で解決してみたい。

まずはObservable使わずmapPropsだけで解決する

ひとまずcounterの計算処理をComponentから剥がしてみよう。

それだけであればObservableは不要で mapProps で十分だ。

    const withTextCounter = mapProps(({ text }) => {
      return {
        text,
        count: text.length
      }
    })
    const TextCounterInner = ({ text, count }) => {
      // const count = text.count
     // カウンターはもう外からもらっているので計算不要
      return (
        <div>
          <div>
            {count}
            文字
          </div>
          <div>{text}</div>
        </div>
      )
    }
    const TextCounter = withTextCounter(TextCounterInner)

更新回数をmapPropsStreamを利用して間引く。

ここで例えば更新回数が多すぎるということでObservableで更新を間引きたい。

今回はRxJSを使う。
不幸にもRxJS@6で上手く動いてないバグを踏み抜いてしまったので、rxjsObservableConfig は今回使わずにセットアップする

    import { tap, auditTime, map } from "rxjs/operators"
    import { mapPropsStreamWithConfig } from "recompose"
    import { from } from "rxjs"

    const rxjsConfig = {
      fromESObservable: from,
      toESObservable: (stream) => stream
    }
    // configを与えてmapPropsStreamを作成
    const mapPropsStream = mapPropsStreamWithConfig(rxjsConfig)

これで mapPropsStream の準備が出来たので、今回のcomponent用のstreamを作成する。

これはredux-observableのEpicととても良く似ている記述になる。今回は audiTimeを利用して200msに一度だけ更新されるようにする

    const withTextCounter = mapPropsStream((props$) =>
      props$.pipe(
        // tap(props => console.log("before props", props)), 
        // -> before props, {text: "aaa"}

        auditTime(200), 
        // 200msの入力をまとめる。throttleやdebounceと似てるが最後にemitするので一番都合が良い

        map(({ text }) => {
          return {
            text,
            count: text.length
          }
        }),
        // textとcountに分岐する。ここは先程のmapPropsと一緒

        // tap(props => console.log("after props", props)),
        // -> after props, { text: "aaa", count: 3 }
      )
    )

    const TextCounter = withTextCounter(TextCounterInner)

カウンターは先程までのものと同じのが利用できるだろう

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした