no-image

ReactNativeでHoCとRecomposeを使う

この記事は、Reactを使っているとたまに耳にするHoCやRecomposeってどんなもの?って人向けに概要をざっくり理解できるような形で書きました。
詳細な使い方などは書いていないので、記事中のリンクなどを参考にしてみてください。

HoCをつかう

HoCってなに?

HoCというのは、Higher-order Componentsの略で、高階関数のコンポーネント版です。

高階関数というのは、関数を引数にとったり、関数をreturnしたりする関数のことで、関数型プログラミングをやってたりするとよく出てくる概念です。

JavaScriptやPythonで標準関数としてあるものを例に挙げるとすれば、map()関数などがありますね。
これは関数とリストを引数にとって、リストを返しています。
ですので、これも一種の高階関数です。

この高階関数という概念をコンポーネントに持ってきたものが、Higher-order Componentsです。
コンポーネントを引数にとったり、コンポーネントをreturnしたりするコンポーネントのことです。

HoCについては以下の記事がわかりやすかったです。

【参考】
View story at Medium.com

なんのメリットあるの?

Reactでプログラミングしていく上で、わざわざHoCを取り入れるメリットはどこにあるのでしょうか。
思いつくものをざっと列挙すると以下のようになります。

  • SFCでコンポーネントを作成できる
  • ロジックとUIを分離できる
  • 再利用性が上がる
  • デザイナーさんと共存できる
  • AtomicDesignと相性がいい
  • Storybookと相性がいい

軽く説明を加えると、Reactではコンポーネントの種類には2種類あります。
これは、ロジックに特化したものか、UIに特化したものかでわけることができます。
ロジックに特化したものをContainerComponentsなどと呼び、UIに特化したものをPresentationalComponentsなどと呼びます。

この後者のPresentationalComponentsはUIの責務を果たすコンポーネントなので、その多くはstateを持ちません。
ですので、わざわざ冗長なclassで定義しなくともシンプルにfunctionで記述することができます。

このようなstateを持たないfunctionで書かれたコンポーネントのことを、Stateless Functional Components、略してSFCなどと呼びます。

例えばこんなやつですね。

このようにして、ロジックとUIを分離したり、シンプルなSFCで書くことによって、コードの使い回しがしやすくなったり、テストを書きやすくなったりします。
また、デザイナーさんもごちゃごちゃしたロジックの部分を気にすることなく、デザインをいじれるようになります。

Reactを使ったプロジェクト構成などを調べてみるとAtomicDesignとか、コンポーネント管理の便利ツールにStorybookとかがあったりするのですが、実際に使ってみると、HoCの考え方はこういうものとの相性がものすごくいいと感じました。

【参考】
Presentational and Container Components – Dan Abramov – Medium
ComponentとContainerについて – Qiita

他にもHoCのキーワードに以下の様のものがあったりするのですが、このへんはややこしいので、後述するrecomposeに慣れてきてから調べてみても良いかなと思います。

  • Props Proxy
  • Inheritance Inversion
  • Render Hijacking

【参考】ReactのHigher Order Components詳解 : 実装の2つのパターンと、親Componentとの比較 | POSTD

考え方

上述したようにHoCというのは、「シンプルなコンポーネント」に「ロジック」を組み合わせていきます。

これをちょっと武士的ななにかで例えてみましょう。
登場人物は、

  • 生身の人間
  • 鎧のようなもの
  • 武士的ななにか

UI部分のみを司るシンプルなSFCが生身の人間です。
そこに一つのロジックを追加する鎧のようなものを必要なだけ装着していきます。
倒したい敵が水に弱いなら水系の武器、火に弱いなら火系の武器、両方必要なら水系も火系も用意します。
これがロジック部分を司るものになります。

生身の人間にこれらの武器を装着することで、目的の武士的ななにかになります。

・・・。
厳密ではないですが、イメージはこんな感じなのです・・。

生身の人間一人に対して、武器が一つ必要なら一つだけでもいいし、複数必要なら複数用意することもできます。
また、これらの武器は他の生身の人間にも適用することができます。
こういったイメージで汎用性を高め、再利用できるようになります。

HoCのお勉強

HoCに対する理解を深めるために調べてみたところ、こんな記事がありました。
この記事内では、HoCを使わないときと使ったときのものを見比べて、その使いみちなどが紹介されていました。
実際に手を動かしてみて感じられたことは以下のようなものでした。

  • このコンポーネントとこのコンポーネントのこの部分ほとんど同じやんというものがあれば、共通化できる
  • UIとロジックを分離できる
  • 一つ一つがシンプルなので理解しやすくなる
  • このブログで紹介されていることに関してはthis.props.childrenでも解決できる。

recomposeをつかう

recomposeというのは、Reactでよく使うようなHoCがたくさん用意されたライブラリです。
「よく使う」というのは、例えばシンプルなSFCに「Stateを追加したい」とか「lifecycleを追加したい」などがありますが、これらの機能を持ったHoCを毎回毎回作るのは大変なので、用意しといたったぞといったものです。

【参考】acdlite/recompose: A React utility belt for function components and higher-order components.

HoC = recomposeなん?

HoCまたはrecomposeで検索すれば必ずと言っていいほどこの2つが一緒に紹介されています。
でもイコールではないです。

HoCはHoC単体で使える概念です。
一方でrecomposeはHoCを用いた便利関数の詰め合わせです。

ドキュメント

公式のドキュメントです。
recompose/API.md at master · acdlite/recompose

ですが、初見の状態からこのページを参考にするのは、正直分かりづらいと思います。
この中からよく使われるものが他のブログで紹介されていたりするので、そこでちょっと触ってみてから、公式のドキュメントを参考にすると良いかと思われます。

導入

$ npm i -D recompose

型定義ファイルは?

TypeScript用の型定義ファイルも用意されています。
$ npm i -D @types/recompose

何ができる?

たくさん用意されていているのですが、例えばこのようなものがあります。

  • propsの追加
  • propsのrename
  • stateの追加
  • lifecycleの追加
  • boolean条件で表示させるコンポーネントを切り分けるロジックの追加

僕がHoCをよくわかっていない状態で、このような一覧を見たとき、え?いつ使うん??って思ったのですが、意識して頂きたいのは、くり返し書いてるようにSFCの再利用性を高めることです。

とにかく抽象的で汎用性の高いシンプルなSFCを作って、そこに状況に合わせたロジックをrecomposeを用いて追加していくことができます。
こういったシンプルなコンポーネントはテストが書きやすかったりstorybookで扱いやすかったり付随するメリットもたくさん出てきます。

recompoeのAPI抜粋

詳細はわからなくても良いので、へぇ、こういうものもあるんだというのを最初に知っておくと、後々必要になったときに探しやすくなるかと思いますので、簡単によく使いそうなものを紹介しておきます。

  • compose
    複数のrecompose関数を一つにまとめられる
    めっちゃよく使います。

  • mapProps
    与えられた配列のpropsを書き換えたり並び替えたりする

  • withState
    stateを追加する

  • withHandlers
    setStateする処理を追加する
    よくwithStateと一緒に使われます

  • defaultProps
    ベースのコンポーネントにpropsを渡す
    withPropsと似ています

  • lifecycle
    ライフサイクルを追加する

  • branch
    何らかの条件(boolean)によって返すコンポーネントを変える

  • pure
    propsが更新されない限り無駄な再レンダリングを抑制する

  • setDisplayName
    componentに任意の名前を付与

  • onlyUpdateForkeys
    不要なレンダリングの削減

  • renameProps
    受け取ったpropを違う名前に変更できる

  • renderNothing
    常にnullを返す

  • nest
    Componentをネスト

【参考】
Recompose でいい感じに Higher-order Components(HoCs)を作る – Qiita
Reactのコンポーネントをステートレスに!!!recomposeとは? ~0からreact習得記 day 09~ – Qiita
recomposeではじめるReact Higher-order Components(HoCs) – Qiita

このサイトを参考にしつつReactNativeでやってみました。

keyがtrueを取るかfalseを取るかで表示するコンポーネントを切り替えるだけのシンプルな実装です。

recomposeなしの場合

まずは比較のためにrecomposeなしで実装してみます。
SFCではなく、Classベースのコンポーネントになります。

recomposeを使う

全く同じ処理をrecomposeのbranchrenderComponentを使って実装してみます。

しっかり理解できなくても大丈夫です。

前者はロジックとUIの部分が一つのclassで実装されています。
一方で、後者は、

  • 単純UIの責務を果たすコンポーネント2つ(NotSupportMessage,CheckPlatformPresenter)、
  • ロジックの責務を果たすコンポーネント(isSupportPlatform)
  • それらを組み合わせるコンポーネント(CheckPlatformContainer)
    に小さく分割して定義され、最後にHoCとしてひとまとめにしています。

一つ一つがシンプルになることで再利用しやすくなります。

recomposeのコツ

APIが多すぎるので、全部の使い方を一気に理解しようとするのはたぶん無理です(僕も半分以上知りません)。
ですので、どんなときに使えるものか、どんな機能があるのかなどをブログやドキュメントを流し見し、かる〜く知っておき、あとで必要になったときに詳しく使い方を理解できたら良いかと思われます。

まとめ

長くなりました。
最初は結構とっかかりにくいので、いろんなブログを参考にして、簡単なものを作りながら感覚を身に着けていくと良いと思います。
関数型っぽい考え方や、運用のしやすさなど、良さげな点が多々見られるのでうまく使いこなしていきたいです。