旧gaaamiiのブログ

間違ったことを書いている時があります。コメントやTwitter、ブコメなどでご指摘ください

Pageコンポーネントの共通処理をHOCにする

Reactでは継承よりも、HOCを使うのが推奨されている。HOCとはHigher Order Componentの略で、コンポーネントを引数にとってコンポーネントを返す関数のこと。

なので、たとえば似たような前処理をするページコンポーネントが複数あったとして、その共通処理を書く場合、

export default class UsersDetailPage extends UsersPage { ... }

みたいなことはせず

export default enhanceUsersPage(UsersDetailPage)

みたいな感じになる。

なんで継承じゃなくて関数...?という疑問は当然出てくるけど、以下に

Note that a HOC doesn’t modify the input component, nor does it use inheritance to copy its behavior. Rather, a HOC composes the original component by wrapping it in a container component. A HOC is a pure function with zero side-effects.

reactjs.org

と書かれて強調されているように、HOCはもとのコンポーネントの振る舞いは一切変えない。

HOCにコンポーネントを渡せば振る舞いが追加されたコンポーネントとして使えるし、HOCに渡さずそのままコンポーネントを使うこともできる。

React Routerを使って、イベントが起きたときにURLを変えつつ何か処理をする

SPAを作っていると、何かイベントが起きたときにURL変えつつなにか処理をしたいということがあります。 たとえば絞り込み検索で、クエリストリングだけ書き換えて画面更新したいというケース。具体的には/blog_posts から /blog_posts?tag=1 へ遷移するような感じです。history.listenにコールバックを渡すと、これが実現できたのでメモしておきます。

まずは実装例

interface Props extends RouteComponentProps {
  hoge: string;
}

class MyPage extends React.Component<Props, State> {
  constructor(props: Props) {
    super(props)
  }

  componentDidMount() {
    this.unregisterHistoryCallback = this.props.history.listen((location: LocationDescriptor) => {
      this.updateMyPageData(location.search.toString())
    })
  }
  
  componentWillUnmount {
    this.unregisterHistoryCallback()
  }
  
  private updateMyPageData(query: string) {
    // ...
  }

  private pushHistory(obj: any) {
    const query = QueryHelper.toQueryString(obj)
    this.props.history.push({ search: query })
  }
}

やっていること

URLつくる -> URLに紐付いた状態更新メソッド的なもの(updateMyPageData)を呼び出している。

やっていること詳しく

ポイントは、URLが変わったときに呼ぶコールバックの登録と解除。それを history.listen というメソッドで行っている。listenは、コールバックを登録してそれを解除する関数を返すので、登録時の返り値を保持しておいて、コンポーネントのunmountのタイミングでそれを呼べば良い。

追記

上記のやり方では props.match.params (たとえばURLが /users/{userId}/posts?hoge=fugauserIdの部分)を、最新のものを参照できなくて悩んでたのですが、以下の記事を読んで、React Routerのlocationを使えばいいことを知りました。

qiita.com

参考

コンビニ人間を読んだ感想

短くてパッと読めて面白かった。主人公が随分変わった人だったけど、共感できるところもあった。生きるの難しいけど、なにか一つでも没頭できる行為なりなんなりがあるのはいいことですな、という気持ちになった。

styled-componentsの良さがよくわからない

と、煽ってるような記事タイトルだけど、 煽りたいわけではなく、単にわからないというつぶやきです。

自分はいまCSS Modulesでやっていて、特に困ることがない。

最近styled-componentsのほうがよく聞くのはなんでだろう。 あっちのほうがなにかいけてるポイントがあるんだろうか。

2018年の振り返り

今年も色々あった。時間過ぎるのがあっという間で、気付いたら10年くらい経ってしまいそうなのでこうして思い出(?)をブログに残しておく。

1月

Reactを書いてた。Angularで書いたものをReact化するという少し変わった仕事だった。他にやるべきことはないか、なぜこれをするのか、という考えが足りなかった気がする。これをやりましょうというのを提案するべきだった。

2月

引き続きReactを書いていた。 組織的には、人がどんどん辞めていった時期。

3月

3月も人が辞めていた気がする。1〜3月の辞め具合がすごかったのでもはや何月に誰が辞めたか正確に覚えていない。

ここで、自分がそれまで携わっていたプロジェクトは開発を止めて、自分は異動する話になった。運良くやりたいことができるところへ異動になり、クビも切られなくてよかった。さすがにクビ切るというのは極端だけど、一瞬その会社での仕事がなくなったのは事実で、いろいろ考えた時期だった。

4月

新部署での仕事が始まる。ちゃんとしたRailsアプリケーションの開発を仕事でやるのは入社したばかりの頃以来で、ひたすら力不足を感じていた。あと会社が資金調達した。

5月

相変わらずひいひい言って大変だった。

6月

わりと大事な機能を開発することになり、これも大変だった。リーダーのレビューが最強で最高で学びが多かった。 難しかったのはオブジェクトの責務分けとか、RESTとして正しいURI設計をするだとか、たくさんテーブルを繋げて条件を指定するようなSQLの書き方だとか。あとRSpecもなかなかうまいこと書けなくて最初は特につらかった。

サーバーサイドの開発をするとき、esaでシーケンス図(UML)を書けて、それが考えを整理したり共有する上でとても役に立った。

7月

shgam.hatenadiary.jp

shgam.hatenadiary.jp

悩んでいる感じがこのブログにも残されている。今思うと、余裕がなかったとはいえwebpackerの設定直さないまま開発を続けていたのはとても良くなかった。

開発していた機能のリリースなどをした。

8月

React, Reduxなプロジェクトの雛形を用意してつく始めたり、Elmのあれをあれしたりしていた。

9月

仕事は引き続き、ReactとReduxのあれをあれしていた。

buildersconへ行ったり、ISUCONに出たりした。

shgam.hatenadiary.jp

shgam.hatenadiary.jp

10月

同じくReactの云々。

11月

Elmで途中まで作っていたものをデモでお披露目するということで、そちらを急いで進めたりなどした。その後のリリースまで持ってくのもけっこう大変で、Elm力が高まった気がする。スケジュール的には押してしまって反省あるのみ。

あと、RubyWorld Conferenceへ行った。

shgam.hatenadiary.jp

あと、結婚したり、歳をとったりもした。

12月

Elmで書いてたやつがリリースされ、Elmのアドベントカレンダーにも記事を投稿したりした。

qiita.com

その後は引き続きReactを書いてた。

会社が御茶ノ水へ移転した。それまでと比べて、戸惑うくらい快適なオフィスになった。 代々木もそれなりに長くいたので感慨深かった。またいつかいそじ行きたい。

shgam.hatenadiary.jp

一年通して

仕事全般について

1〜3月は会社が良い雰囲気ではなかった。業界的に人が辞めるのはよくあることかもしれないけど、それにしてもこんなに一気に…という感じだった(年の後半では人が増えてオフィスも移転して、雰囲気も明るくなって本当に良かった)。 当時は、困った、さあこれからどうするって感じで、そこで自分が関わってきたところも、問題を洗い出したりして、開発を止めることになり、そこを離れるということになった。

その時は、ああこうやって仕事って無くなるんだなと、はっきりとした危機感を持った。 事業的にいろいろ整理が発生するのは絶対あることだろうけど、突然仕事が無くなったときに、どこにも行き場がないみたいな状態にならないように精進していかなくてはと思った。今回に関しては運が良く、希望のところへ異動させてもらえた。

ウェブフロントエンドについて

自分の仕事に危機感を持ったこともあり、ウェブフロントエンドに関して考えることが多かった。 そこで考えて出てきたのは、やっぱりウェブアプリケーション使うなら、使いやすく、速く感じられるのが良いだろうということ。そのために必要な技術がウェブフロントエンドの諸々だと思っている。

iOSAndroidのネイティブアプリだったら、はじめからサーバー側のロジックととクライアント側のロジックは切り離されてるけど、古くから(クライアントMVCとかSPAが持て囃される以前)のウェブアプリケーションだと、サーバー側のコードの一部として管理されていて、密結合だったりする。すると、クライアント側での最適化みたいなのは難しく、基本的にユーザーは毎回サーバーから手元のブラウザへ、HTMLドキュメント全体をダウンロードして、画面を更新することになる。

Fluxみたいなパターンと仮想DOMみたいな仕組みで、ユーザーの行動に応じて画面のなかの必要な分だけ更新するようなアプリケーションが可能になった。ただウェブにはURLがあるので、フロントエンド側にルーターを持ち、ウェブのURLの表現を保つ工夫も必要になる。

もちろんSPAみたいなのは良いことばかりではなく、サーバーがリクエストに応じたHTMLドキュメントを返すだけというシンプルさを捨てることになる。実装の複雑度は増すし、ユーザーからの目線で考えてみても、SPAのほうが体験が良いと言えるのはちゃんと作られている場合だけで、不具合があったり不自然な画面遷移をしたりするものを作ってしまうと、SPAなんかしなけりゃよかったということになりかねないとは思う。それでも、自分はサーバーとクライアントは分けたほうが良いと思っている。APIと画面を分けるのは、サーバーはサーバーを、クライアントはクライアントのことをうまくやるのに集中できる環境をつくることだと思う。

しかし、モバイルで最高の体験を、と考えるならやっぱりウェブよりネイティブアプリだと思っていて、じゃあ上で書いたようなものは何をつくるために必要なのかと考えると、PCで使うSaaSみたいなものがあってると思う。「クラウドなんちゃらシステム」みたいなもの。そして、人がパソコンのキーボードを叩くのは多くが仕事かそれに近いようなときで、それ以外はみんなスマホを使ってる(と思う。すごい雑認識だけど)。だから、ウェブフロントエンドをさらに個人的な視点で狭く言うと、仕事でPCで使うときに使うシステムを使いやすくする技術になる。

今まさにやっている仕事もそれに近いものなので、これをどこまでの出来にできるかというのが、事業にも、自分のキャリアにも重要になっていると思ってる。なかなかうまくいかないこともあるけど、引き続き頑張っていきたい。

プログラミング言語について

Ruby(Rails)は、よく考えてる人が書いたものは洗練されていて、責務がしっかり分けられていてきれいという感想を持った。オブジェクト指向の本読みきらねばという気持ちが強まった。

あと、一時期Elmを書いたことにより以下のような気持ちが芽生えた。

  • 静的型付け言語良い
  • Lintとかなるべく自分で設定する必要がなく、コードのフォーマットとかは自動でされたほうが嬉しい

関数型言語についての感想は、関数の入力の型がその入力を制限していて、同じ入力に対して常に同じ出力になるっていう単純さが良くて、ほかの難しそうなことについては正直よくわかっていない。

特にオブジェクト指向デザインパターンのような、プログラム全体の整理整頓みたいなのをどうやるべきかというのがまだよくわかってないけど、Elmの場合は中心となる型があり、それをいじくる関数が一緒にまとまったものがモジュールになるという考え方っぽかった。

来年はGo言語も少し触ってみたいという気持ちになってる。

健康

大きい病気はしなかったけど、地味に疲れやすい体になってきてるなという気はした。 健康はなんかぼーっとしてるとどんどん奪われていくので、きっと健康オタクくらいの感じがちょうどいい。

生活

今年は結婚をしたので、人生設計を堅くしてこれからのことに備えていきたい。

人間関係

ありがたいことに、仕事でも親族関係でも友人関係でも、嫌いな人と接することがほぼない。とても恵まれていた。

インターネット上での活動

Podcastとかほとんどとれなかった。

きまべんという思いつきで始めたScrapboxプロジェクトが、熱意のある方々によって続けられていて、自分もやっていかなくてはと刺激される。

まとめ

今年もお世話になりました。来年もよろしくお願いします。

レイアウトのためのatomコンポーネントとかtemplates層とかの実装

Atomic Designの本「Atomic Design ~堅牢で使いやすいUIを効率良く設計する」を参考にしながら、こういう感じでやっていってる。

// atoms/AppLayout.tsx
const AppLayout = (props: Props) => {
  const children = React.Children.toArray(props.children)

  return (
    <div className={styles.left}>{children[0]}</div>
    <div className={styles.right}>{children[1]}</div>
    <main>{children[2]}</main>
  )
}

// templates/UserPageTemplate.tsx
interface Props {
  user: UserInterface;
}

const UserPageTemplate = (props: Props) => {
  return (
    <AppLayout>
      <AppHeader />
      <AppSidebar />
      <UserView user={props.user} />
    </AppLayout>
  )
}

AppLayout に当てるCSSのflexboxなりgrid layoutなりでレイアウトを実現しておいて、同じレイアウトのページを作成するときは同じようにAppLayoutで囲えばそのレイアウトになるという感じになる。

templateのコンポーネントでは、どのレイアウトにどのOrganismを置くかということを記述する。URLから最初の状態を作ってpropsとして渡すのはその上のpageコンポーネントにやってもらう。なのでtemplateはStateless Functional Component (状態無し関数コンポーネント)になる。

レイアウトがAtomだってことに最初はちょっと違和感あったけど、別に何もおかしなことはなかった。

代々木の思い出

オフィス移転のため、今日が代々木オフィス最終日だった。

今の会社に転職してから色々あったけど、いそじというラーメン屋のつけ麺がずっと安定して美味かった。

26歳

忘れてた。26歳になっていました。若者を自称するのはもはや不可能になり、身体能力も着実に衰えてきています。この前テニスしたら大学生にロブで振られてボロボロにされ、負けました。翌々日の筋肉痛が全てを物語っていました。つらい。26歳も頑張ります。

キウイの本を読んでる

キウイ🥝。

なんで読んでるのか

自分はいまウェブアプリケーションのUIをつくる仕事をしているので、ReactやらReduxの記事とかはよく読む。けどもう少し広めの、UIをつくるプログラミング全般に適用できる設計パターンみたいなのを学びたかった。

あとで感想ここに書く

Storybookを使って気付くCSSの粗さ

がっと作った大きなOrganismがあって、それを構成するMolelculeやらAtomやらのレベルのコンポーネントをすべてStorybookに置いた段階で、「あれ、なんかこれいい感じに表示できない...」ということに気付く。CSS Modulesを使ってスタイルのスコープをそのコンポーネントに閉じさせているものの、それだけでは期待する見た目にならない。正しい見た目が上の要素に依存していて、それがないとちゃんとした見た目にならない。直さなくては...ということになる。

Storybookに細かい単位でコンポーネントを置かなければこういうのをごまかせてしまうので、いざ細かいコンポーネントを使い回すときになるまで「なんだこれ」ということに気づかなかったりする。Storybookの使いみちはいろいろあるけど、こういうスタイルのスコープ確認につかえて便利だと思う。

fetch-mockでStorybookからモックデータを利用する

新たに開発している画面で、Storybookを利用してデザイナーさんに見た目のレビューしてもらいやすくしようとしている。この画面はAPIと並行して開発しているので、モックデータを用意してそれを見るようにする必要がある。

アプリケーション本体の方ではjson-serverを使っていて、Storybookもローカルで確認するにはそれで十分だったのだけど、確認のために用意した環境はS3なので、別でモックサーバーを立てるというのはそこまでするの感がある。

ferch-mockを使うと、APIへのリクエスト部分をモックのpromiseに差し替えられる。setTimeoutの値を変えれば遅いAPIをシミュレーションできるし、エラーを返せばエラー時の挙動を確認できる。便利。

json-serverのために用意したモックデータをそのまま使えてよかった。

ContainerコンポーネントとPresentationalコンポーネント、reduxとの接続についての方針とか

ReactとReduxでアプリケーション書いていて、containerコンポーネントとpresentationalコンポーネントの分け方どうするかという話。スマホで雑に書いているので、あとでまとめる。

元ネタ

redux作者のブログ記事のあれ。

参考にしたもの

AbemaTVの人のAtomic Designの本に書いてあった方法

コンポーネントの種類分けることで何ができるのか

もう少し詳しく

  • イベントが起きたときに何をするかというのをcontainer側に書いておいて、見た目のコンポーネントにはそれをpropsとして渡す
  • reduxのstateに変更を加えるような処理はactionを投げてreducerが処理をする
  • 見た目コンポーネントがそのコンポーネント自身でしか使わないような見た目のStateを持つのは許容する。
  • ファイルは今のところ分けずに、1ファイル内にcontainerもpresentationalも書いてる
  • reduxと接続するのはcontainer