メディアクエリを扱うReact Hooksのカスタムフックを作った話

React Hooksでぼちぼちコードを書いているのだけれども、今日カスタムフックの実装をしてようやくReact Hooksの勘所が少し理解できたような気がするので、忘れないうちにメモ代わりに書いています。

今実装しているコードで、画面幅に合わせて異なるReact Componentを返したいケースがあって、最初は画面幅をwindow.innerWidthで取得して場合分けするコードをcomponentDidMountに記述して実現していましたが、ロード後に画面サイズを変更した場合にそれが反映されない問題がありました。 基本的にはデバイス毎に異なるコンポーネントをrenderしたり、propsの値を変えたいだけなので、それほど大きな問題にはなっていませんが、 出来ればなんとかして動的な画面サイズの変更に合わせて切り替えられるようにしたいなあと思っていました。

その実現のためにuseMediaQueryというカスタムフックを実装してみました。 引数には区別したいメディアクエリの配列を渡し、返り値にその中で現在のメディアクエリを返すようになっています。 画面幅の変更等でマッチするメディアクエリが変わった場合にはuseMediaQueryの返す値が変わり、Reactコンポーネントが更新されるようになっています。

割とシンプルなコードで実現できました。 以下がuseMediaQueryの実装です。

import { useEffect, useState } from 'react'

const getCurrentMedia = (mediaList) => {
  let result = null;
  for (const media of mediaList) {
    if (window.matchMedia(media).matches) {
      result = media
      break
    }
  }
  return result
}

const useMediaQuery = (mediaList) => {
  const [current, setCurrent] = useState(getCurrentMedia(mediaList))

  useEffect(() => {
    let mounted = true
    let timeout
    const onResize = () => {
      // 500msに一度しかresizeイベントのcallbackを実行しないようにする
      if (timeout) return

      const media = getCurrentMedia(mediaList)
      if (current !== media) {
        setCurrent(media)
      }

      timeout = setTimeout(() => timeout = null, 500)
    }

    window.addEventListener('resize', onResize)

    return () => {
      mounted = false
      window.removeEventListener('resize', onResize)
    }
  }, [])

  return current
}

export default useMediaQuery

実装はreact-useuseOrientationを参考にしました。 ReactのVirtualDOMの外の世界であるwindow.innerWidthやresizeイベントをカスタムフック内に隠蔽し、 useStateを利用してその結果だけをReactの世界で管理するようになっています。 これにより、実装時にはresizeイベントのことを気にせず、文字列としてメディアクエリを取得することが出来るようになります。

利用する際は以下のように表示を切り分けたいメディアクエリの配列を渡すことで、現在の状態にマッチするものが返されるので、その値を利用して描画処理を行います。

import { useMemo } from 'react'
import 'useMediaQuery' from './useMediaQuery'

const QUERY_SP = "(max-width: 640px)"
const QUERY_PC = "(min-width: 641px)"

const Comp = (props) => {
  // queryには現在の画面幅に応じて、QUERY_SPもしくはQUERY_PCの文字列が返される
  const query = useMediaQuery([QUERY_SP, QUERY_PC])
  const text = useMemo(() => {
    switch(query) {
      case QUERY_SP:
        return "スマートフォン"
      case QUERY_PC:
        return "PC"
    }
  }, [query])

  return <span>現在は{text}で表示しています。</span>
}

export default Comp

こんな感じで、実装している側ではresizeイベントを全く考慮すること無く実装することが出来ました。

結構個人的には革命的だったというか、Reactでどうしても実装が綺麗に出来ないと思ってたところがとてもすっきりしたので、 仕事中にもかかわらず思わず興奮してしまいました。

そのうちuseMediaQueryはテストコード書いたり、言語をTypeScriptで書き直した上でGitHub上で公開しようかなとは思っています。 カスタムフックのライブラリってどういう粒度で公開したらいいのかちょっと悩ましいですね。 フック1つでライブラリ1つにすると、なんか大量のライブラリを管理しなきゃいけなさそうで面倒だなあとは思いつつ、 react-useみたいにユーティリティライブラリみたいにする場合、他にどういうカスタムフックとセットにすべきかというのもなかなか悩ましいところ。

技術書典7に参加した話

去る9/22に行われた技術書典にサークルとして参加してきました。 「Webサービスをささえる運用設計」という本をなんとか間に合わせて頒布しました。

今回、この手の同人誌即売会に初めて参加すると言うことで、まずは一通り一人で全部やってみようと試みました。 表紙のデザインとかを他の人にお願いするということも考えたのですが、どういった作業プロセスがあるのかを把握しておきたいのと、 単に本の装丁をやったことがなかったので、チャレンジしてみたいという気持ちがありました。 最初にやるときはまず一人で全部やってみたいと思うのは、多分敬愛する新海誠さんの影響だろうなあ、と。

サークル参加しようと思ったきっかけは4月に参加したRubyKaigiで、スポンサーブースを見て回ったときに色んな会社さんが技術同人誌を置いていたのを見て、 なんとなくエンジニアのポートフォリオとして技術同人誌の一冊でも書く時代なのかなあと思ったことが発端です。 もちろんそれだけでなく、もうちょっと自分の考えていることや持っている知識をアウトプットしたいという気持ちもありましたし、 単に本を作るということに憧れがあったということもあります。

そんなこんなで技術書典7に勢いでサークル参加の申込を行い、うっかり当選してしまって退路を塞がれてしまったので、 技術同人誌の執筆にとりかかることになったのでした。

テーマについては色々候補があり、自分の趣味に走っても良かったのですが、 ちょうど会社でWebサービスの運用コストや難易度をどうやって下げるかという話をいろいろな人にしていたタイミングだったので、 その内容を本にまとめようと思い、今回の本の執筆に至ることになりました。

本のタイトルに「運用設計」とあるとおり、伝えたいことは「Webサービスの運用をちゃんと行うためにはどんな環境を構築すればいいか」でした。 とはいえ、初めての人でも実際に手を動かして試せるよう座学よりは実践的な内容にしようと思いました。 今から考えるとちょっと実践によりすぎて、初心者向けのWebサービス環境構築入門っぽい本になってしまいました。 もうちょっと「何故そのような環境を構築する必要があるのか」ということを深掘りできればよかったなあと思っています。

とはいえ、運用にまつわる面倒なあれこれを様々なサービスを利用することで構築・運用コストを大幅に下げ、 環境の質を大幅に上げるプラクティスとしてはそれなりによく書けたんじゃないかなあと思っています。

本の印刷は日光企画さんに依頼しました。 オフセット本の印刷など初めての経験だったので色々知らないことや戸惑うことも多かったですが、 特に印刷会社さんに怒られることも無く無事に入稿完了しました。 表紙の加工の種類とか背表紙をどうやって作るか等、全然知らなかったのでいい勉強になりました。

技術書典当日ですが、事前に色んな人から「売り子さんは絶対に自分以外にもう一人用意しておいた方がいい」と言われていたのですが、 パッと頼めるアテも思いつかず、結局ソロ参加しました。 15時くらいには人が少なくなって、ちょっとくらいブースに人が居なくても大丈夫だろうと見込んでいたのですが、 実際には人が切れるタイミングが全くなく、結局開始から終了までずっとブースで立ちっぱなしで対応していました。 体力的には平気だったのですが、知り合いのブースに挨拶と買い物に行けなかったのはちょっと残念でした。 特に向こう側からはわざわざ買いに来てくれたりもしてたので、その辺りはちょっと不義理だったかなあと反省しています。

最終的には赤字にならない程度に頒布することができて良かったです。 実際に手に取ってくれる方の対応をしていると、赤字黒字と言うよりも買ってくれた方がたくさんいるという事実が嬉しかったです。 本1冊買ってもらうことがこれほど嬉しいとは参加前には想像していませんでした。 少しでも買ってくれた方のお役に立てると嬉しいです。

バタバタしていて12月の技書博には申し込み損ねたのですが、そこそこ在庫があるのでまたどこかの同人誌即売会に参加できればいいなと思っています。 次はもうちょっと趣味に走った本を書くか、運用系の深掘りした本を書くか、色々悩ましいところです。

改めて、「Webサービスをささえる運用設計」を手に取ってくれた方、当日遊びに来てくれた方、色々お手伝いをしてくれた方に感謝です。 次の機会がありましたら、またよろしくお願いします!

テレビアニメ「氷菓」の話

京都アニメーションが制作した「氷菓」は米澤穂信さんの「氷菓」「愚者のエンドロール」「クドリャフカの順番」「遠回りする雛」(<古典部>シリーズ)を原作とするテレビアニメーションで、米澤穂信さんと京都アニメーションのファンである自分がすごく楽しみにしてた作品でした。

いわゆる「日常の謎」と呼ばれるミステリにカテゴライズされる作品で、謎解きの部分の面白さや、心を針で刺されるような鈍い痛みを伴うお話を、本当にちゃんと映像化できるのか、楽しみにしながらも不安を抱えて待っていたことを思い出します。

実際に完成したアニメは、もちろんミステリの謎解きの部分の表現に関しては小説の方が圧倒的にアドバンテージがあるものの、米澤穂信さんの最高の魅力だと思っている「なぜそれをやったのか」という部分においてものすごい密度と繊細さで描かれており、原作の魅力が余すところなく伝えられるそんな作品でした。

アニメ終了後も<古典部>シリーズは続いており、「ふたりの距離の概算」「いまさら翼といわれても」と続きも書かれています。シリーズは主人公の折木奉太郎の高校卒業まで続くらしく、それらの作品をまた京都アニメーションによるアニメ作品としても見られたら嬉しいなとずっと思っていました。

心動かす素敵な作品を、本当にありがとうございました。青春の苦さを鮮やかな色彩で切り取ったあなたの作風が大好きでした。 心よりご冥福をお祈りいたします。

「天気の子」の話

新海誠監督の最新作「天気の子」を金曜日の公開日に渋谷TOHOシネマズ朝9:00からの回でを見てきました。 「君の名は。」で一気に有名な監督になってしまい、舞台挨拶の回のチケットが取れなくなってしまったのは残念なところ。

今回は「子供が気持ちいいくらいにワガママを突き通し、まわりの素敵な大人達がそれを全力で支える」お話しだなあと感じました。 ずっと実年齢より精神年齢の方が高いキャラクターを主役クラスに置く傾向があったので、なかなか新鮮でした。

何か一つ大きな事件を乗り越えて、一回り成長した自分が現状を前向きに受け入れる、というエンディングは、 「彼女と彼女の猫」「ほしのこえ」「秒速5センチメートル」あたりでも描いていた新海監督の普遍的なテーマで、 過去作品に比べてそれがもの凄く伝わりやすかったんじゃないかなと思います。 新海監督、過去作のフィードバックを受けて貪欲に作品をリファインしていく姿勢が本当に凄いなあと思いました。

過去作ファンのためのファンサービスも旺盛で、妥協せずにあらゆる人に楽しんで貰おうと思って作られてるんだなあと感じました。 新海監督の作品は、作品そのものの出来もそうなんですが、それを通して見える監督の作り手としての姿勢が本当に好きで。

ところで、新海監督の描く都会の景色(主に新宿)が本当に綺麗なのはいつものことなのですが、 今回凄いなと思ったのは映画を見終わって現実に戻り、劇中で描かれた場所と同じ風景を目にしたとき、それがめちゃくちゃ美しかったんですよ。

過去のトークショーで新海監督は「自分が辛いときに街の景色に励まされた」と言い、それを積極的に描く理由の一つとして挙げていました。 それが劇中の絵を通して共感を呼び、実際にその景色を自分の目で見たときに、同じように美しいと思えるようになってきたんです。 改めて共感を呼ぶ力が本当に強いなと感じた出来事でした。

また何回か見に行きたいですね。 複数回見ても、見るたびにいつも新しい発見があるので。

ただ精神の安定のために書いただけの話

アニメ等の作品をただの娯楽では無く、自分を構成するためのかけがえのないものにしている人はたくさん居ると思います。 そういう自分のそのうちの一人で、あの作品に出会えたから今の自分がある、とはっきり言える作品がいくつかあります。

自分にとっては「けいおん!」がそれの一つで、当時大して上手でもなく年に一度程度のライブをするくらいのバンドをやってた自分にピンポイントで刺さりました。 楽器から音を出して、人と合わせることが心の底から楽しいことを、あそこまで丁寧に描画した作品に会えたことに心が震えたことを覚えています。

その、京都アニメーションの話。

自分を構成する要素を作ってくれた人たちが、大変不幸な目に遭われてしまいました。 とても悲しく悔しい気持ちで一杯だし、同じ思いをしている人が日本中、世界中にたくさん居ることを考えると心が痛みます。

もの凄い無力感に襲われましたが、8年前の地震で自分は「変わらない日常を過ごすこと」の大切さを学びました。 そして今の自分は仕事で様々なプロダクトを作ったり、個人では技術同人誌を書こうとしている立場です。 形は違えど、同じものを作る人間として、今手を止めるわけにはいかないという意地みたいなものが湧いています。

お亡くなりになられた方の冥福を祈ります。 そして怪我をされてしまった方の無事を祈ります。 二度とこのような悲惨な事件が起こらないことを心から祈ります。

最近の話と技術書典に当選した話

今年に入ってからしばらく、会社以外の外出を全くする気が起きない現象が起こってました。 割と酷く忙しい目に遭ってたとか、その他諸々思い当たる理由が無くも無いのだが、 まあこういうものは起こるときは起こるものなので、そういうものだと諦めるしかないのです。

仕事も少し落ち着き、未だにそれ以外で外に出る気も起きてはいないのだが、 なんとなく気力的なものとかが戻りはじめてきたので、少なくともオンライン上の露出くらいは戻そうかなと。 なお、外出する気力が無かったおかげで外食が減り、結果的に健康的な食生活が遅れているのはなんとも。 健康診断に引っかかりまくって、生活習慣の改善をしてたというのも大きいけれども。

リハビリの一環というわけではないのですが、9月に行われる技術書典7に出展者側として応募し、当選しました。 なので2ヶ月後までに本を1冊仕上げなければいけなくなったわけです。 今のところ、Webアプリケーションの運用にまつわる話を書こうと思ってます。

内容としては主に、

  • アプリケーションのデプロイの自動化
  • スケールアップ
  • 簡単な死活監視
  • ログの残し方、集計、検索等
  • バックアップと復旧

みたいなことを、外部サービスをうまく使いつつ、手間とお金をあんまりかけずにやる実践的な方法をまとめる予定です。

使われているサービスというものは、頻繁に開発中に想定していなかったことが発生します。 想定外の出来事に対して、どれだけ迅速に対応出来るか、ダメージを抑えられるか、今後の改善につなげられるかという点で、 ただアプリケーションを実装するだけだと気づきにくいようなポイントを紹介できればと考えています。

Webアプリケーションに限った話ではないですが、サービスというものは作って終わりでは無く、 使われることでいろいろな問題が起こり、それを解決することで育てていくものだと思います。 開発者からのスポットがなかなか当たりにくい運用まわりの話ですが、実際のサービス運用で実施したり気づいたりしたこと、 それを実際の開発にフィードバックした話なども出来る範囲で書ければいいかなと思っています。

あと、技術書典に出展する上でサークル名を付ける必要があったので、ついでのこのblog名を変更しました。 パスタ系男子が一番お世話になる具材の名前ですね。あと、学生時代にお世話になったパスタ屋の看板メニュー名でもありますね。 学生時代はカウンターに座って調理過程をひたすら眺めて作り方を頑張って覚えてたりしてたので、ある種自分の原点的なところでもあります。

とりあえず今月中にサークルカット作らねば。

知識の深さと広さの話

久しぶりにポエム書く!

開発者としてなにかものを作る際に自分が担当してない箇所の知識をどれくらい持つべきかと言うことをちょっと考えてみました。開発者の採用面接してる中で、自分が候補者に対してものを作る上で必要となる一通りの知見を求めていることに気づいたので、どうしてそのように考えているのか整理しようと思ったのがきっかけ。

ものを作るに当たって、うちの会社はディレクターやデザイナーなど、他の役割の人とチームを組んで、それぞれの役割を担っていくことが多く、彼ら彼女らとうまくコミュニケーションをとることがプロジェクトの質を上げていくことに繋がると思っています。で、人とうまくコミュニケーションをとる際に重要なのが共通の知識だと考えています。

仕事をすすめるにあたって、役割分担の境界ってあるじゃないですか。責任範囲と言うよりは、内容的にどの職種の人が担当するか悩ましい場所。うちの会社だと例えばHTMLやCSSのコーディングやUI系のJavaScriptの実装なんかはプロジェクトによって結構開発者がやるかデザイナーがやるかが変わってきます。画面仕様考えたり進捗管理したりみたいな仕事はディレクターの状況を見て任せたり引き取ったりとまちまちです。

そのような境界にあるような仕事内容はそれぞれの職種の人の間でコミュニケーションが頻繁に行われる箇所でもあります。その境界の仕事について、開発者とデザイナー、開発者とディレクターが共通の知識を持っていないと仕事を進めるにあたっての必要なコミュニケーションをとることは出来ません。その場合、コミュニケーションの質が下がるか、コミュニケーションコストが増大するか、その両方か、いずれかの状態に陥ってしまいます。

もちろん、そんな完璧超人みたいな人がほいほい居るわけもなく、自分が出来ているかというと全然出来ていないわけですが、大事なのはそうあるべきという姿勢なのかと思います。自分の学習、成長に「これは知らなくていいや」って線を引かない人であろうとすることが大事なのかなと思っています。