jQueryを卒業したかった僕がReact StaticでReactをイチから学んでWebサイトを作った話

Typetalk チームフロントエンドエンジニアの岡藤(@johnykei)です。先日 Typetalk Webサイトリニューアルに伴い、フロントエンドの技術を jQuery から React + styled-components に刷新しました本記事では、React を用いた Web サイト制作についてお伝えします。

はじめに

タイトルにもあるように、僕は今まで JavaScript を書く必要がある時は使い慣れた jQuery を使っていました。

フロントエンドエンジニアという肩書きではありますが、フロントエンドエンジニアという職種は幅広く、僕はどちらかというとページやUIのスタイリングが主な業務で、普段 JavaScript を書く頻度もそんなに高くありませんでした。

最近海外ではそのような業種の人をフロントエンドデザイナーと呼ぶ動きも出てきていますが、jQuery しか使っていない自分を果たして「フロントエンドエンジニアです」と胸を張って言ってもいいのか?と思うこともよくありました。

以前の Typetalk サイトejs + Sass + jQuery でタスクランナー( gulp ) でコンパイルするというよくある構成で、当初はそのまま同じ環境でリニューアルを進めようとしていました。ところが、同僚が Wiki 入門のページを React で作ったというのを聞いて衝撃を受けました。

そもそも React や Vue.js などを使った開発に興味はありましたが、Web アプリケーションで使うものだと決めつけていて、静的サイトも React で作るという発想はなかったからです。React で作られたそのページのパフォーマンスは、「僕もSPAなサイトを作りたい!」と思わせるくらいかなりの影響を与えました。

また、まさにそれと同じタイミングに React を使った静的サイトジェネレーターの React Static がリリースされたということもあり、「これは早速使ってみるしかない!」と思ったのが導入した背景です。( Sites Built with React-Static に Typetalk も載せていただきました😆 )

では、実際にどのように React を使って静的サイトを構築したのかをお話しします。

本稿について

対象読者

  • フロントエンド初心者
  • React や Vue.js などのフロントエンドライブラリ/フレームワークに興味はあるけど、なかなか手を出せていない人

書いていないこと

  • React とは?
  • React と jQuery の違い
  • React Component の話
  • React 以外のフレームワークの話
  • 型の話

React Static

React Static とは

React Static とはその名の通り React で静的サイトを作る為のフレームワークです。似たようなツールは Next.js や Gatsby などが有名です。

React で静的サイトで作ることで何が嬉しいのでしょうか?React Static の Features に書かれている項目をいくつか見てみましょう。

⚛️ 100% React (or Preact!)
🚀 Blazing fast builds and performance.

React は Virtual(仮想) DOM という技術が使われていて、DOM を変更する際に必要箇所の差分のみを変更することができます。その結果、不要な DOM の更新が走らないので、ページのパフォーマンスが向上します。よって生の DOM よりも早くページを表示できます。ぜひ、その早さを Typetalk のサイトで実感してみてください。
(また PageSpeed Insights のテストでもモバイルは99点という高水準の結果が出すことができました。)

🎯 Built for SEO.

また、SPA で心配されがちな SEO ですが、その辺も React Static はきちんと考えられて作られているので特に問題はありません。

🥇 React-first developer experience.

もうひとつ付け加えると、あなたは今後 React を使った Web アプリケーション開発に携わることがあるかもしれません。その時に React で静的サイトを作った経験があるならばきっと役に立つと思います。その為の React 勉強用の環境としても最適です。

React Static の環境構築

まずは、公式ドキュメントにもあるようにターミナルで手元に npm もしくは yarn を使って React Static をインストールします。(今回は yarn を使用します。また、React Static のバージョンは v5.6.1 を使用しています。)

インストールが終わったら、早速プロジェクトを作ってみましょう。以下のコマンドで作り始めます。

対話形式でプロジェクトの作成が始まります。まずはプロジェクト名を入力します。

次に使用するテンプレートを選びます。今回は後述する styled-components を選択します。この他にも使用できるテンプレートはこちらにまとまっていますので参考にしてください。

上のメッセージが出たら設定は終わりです。たったこれだけで面倒な Webpack の設定も全て自動でやってくれます。早速作られたディレクトリに行って開発環境を立ち上げてみましょう。

無事に立ち上がったら http://localhost:3000 にアクセスしてみましょう。以下の画像のような画面が表示されてたら成功です。

React Static ScreenshotReact Static の初期画面。この画面が表示されたら成功

簡単にディレクトリ構成を説明しておきます。インストールが終わると以下のような構成ができていると思います。各ファイルやディレクトリの役割も以下に簡単に書いておきます。(node_modulesREADME.md 等は省略しています。)

デフォルトの構成だと基本的には containers ディレクトリ内にそのページ用のコードを書いて、ヘッダーなどの共通部品は App.js に 書いていくことになります。ただし今回は styled-components を使ってコンポーネント指向で書いていくのでこのようなやり方はしません。具体的には src ディレクトリ以下に components というディレクトリを作ってそこで作業していくことになります。この辺は「コンポーネント設計」の項で詳しく説明しますので、現時点ではそんなものかというくらいに考えててもらっても構わないです。

これで準備ができました。これだけで最低限のサイトは作れると思います。ここから作るサイトに合わせていろいろ設定を加えていくことになりますが、今回の記事では長くなってしまうのでこれ以上 React Static の設定周りの話は書きません。React Static がサイトを作る上で便利なコンポーネント(LinkHead とか)を提供してくれているので、詳しくは公式のドキュメントを読んで実際に試してみてください。

React の最低限の知識

React を使って実際にコードを書いていくことになりますが、最低限覚えておかないといけない知識がいくつかあります。ただし、コンポーネント(パーツ)の DOM の定義とスタイリングくらいであれば簡単な JavaScript が書ければ覚えることはそんなに多くないです。僕も書くまでに何か特別に学習したわけでもなく、実際にやりながら覚えることができましたので、難しそうだからと諦めるのではなく、まずは何事も手を動かして触ってみることが大事ですね。

基本的な書き方

React でコンポーネントを作るには、以下のようなシンプルなコードで書くことができます。

※ 以降、サンプルコードでは ES6 の文法(アロー関数など)を使用します。

これは何をやっているかというと、MyHeading という関数を作って、その中でこのコンポーネントの DOM ツリーを定義しているだけです。他に React.Component を使って定義する方法もありますが、そちらは一旦無視してもらっても大丈夫です。僕も今回のサイトではほとんど使うことありませんでした。

これくらいで書けるのならやれそうな気がしてきませんか?ただ、この中で書いてある propsprops.children にあまり馴染みのない方もおられるかもしれません。この props は React を書く上で非常に重要なので覚えておきましょう。

※余談ですが、上のコードはもう少しシンプルに書くこともできます。この辺は ES6 の話になるので今回は詳しい説明はしないでおきます。

Props

Props はコンポーネントの状態を表すものです。同じようなものとして State というものもありますが、State はそのコンポーネント自身が持っているもので Props は他者から受け渡されるものとなります。基本的に Props は親コンポーネントから渡されるものと思った方が理解しやすいかもしれません。以下は MyHeading を使うコンポーネントのサンプルコードです。

この MyIntro というコンポーネントで MyHeading を import して使用しています。そして、この <MyHeading> タグ内の「あいうえお」というテキストを MyHeading コンポーネントの children という Props に渡している(代入している)という仕組みです。この「あいうえお」を「かきくけこ」に変更すれば children も更新されます。

図:Props の受け渡しの解説Props の受け渡しの解説図

ただし、このように Props を更新すると、その更新したコンポーネントの DOM ツリー以下が全て再描画されます。なので設計次第ではパフォーマンスが落ちてしまうことになるので注意が必要です。そのためきちんとコンポーネントの設計してコンポーネントの単位をできるだけ小さくするのが重要になります。その辺は「コンポーネント設計」の項で詳しく説明します。またこの「ライフサイクルイベント」に関しては公式のドキュメントに詳しく書いていますのでそちらをお読みください。

今作ったサンプルのデモは以下で確認できます。(コード整形ツールの Prettier が自動で効いているので上で書いたコードと少し違いがありますが、やっていることは同じです。)

デモを別ウィンドウで見る

今回 State に関しての説明は割愛します。なぜなら今回僕がサイトを作る時において State はほとんど使用しませんでした。Web アプリケーションでは Redux を使って状態管理をする必要がある場合はありますが、あくまで今回は静的サイトなので状態を管理する必要もほとんどありません。またそうでなくても State をできるだけ書かずにステートレスなコンポーネントで書いた方がシンプルに書けてパフォーマンス的にもいいと言われています。そしてロジック側の実装ではそれと一緒に Higher-order Components (HoC) を使っていくことになるのですが、この辺は今回のサイトリニューアルでロジック実装を担当したメンバーがいつかブログを書いてくれることを期待します。

styled-components

React コンポーネントの作り方はわかりました。あとはここにスタイルを入れていけば UI の見た目的には完成します。ここからは styled-components を使った React コンポーネントのスタイリングについて説明していきます。

React コンポーネントのスタイリング方法

その前に React コンポーネントのスタイリング方法について少し説明します。やり方は大きく分けて4種類あり、それぞれメリット・デメリットがあります。

  • クラス(className)
  • インラインスタイル
  • CSS Modules
  • CSS in JS

クラス(className)

こちらは従来のやり方です。DOM 側で class をつけて、その class に対して CSS や SASS を使ってスタイリングしていきます。ただし、React の DOM の定義は JSX で書いている(先ほどのサンプルの return () の中の部分 )ので、クラス名を指定する場合は class 属性ではなく className 属性を使います(class は予約語となっているため使えません)

従来のやり方なのでやりやすいと思われるかもしれませんが、今までに悩まされて来た CSS 特有の問題がそのまま残るのであまりおすすめできません。CSS でやってきた方ならわかるとは思いますが、CSS は全てグローバルに適用されるので、きちんと CSS 設計をしないとすぐに破綻してしまいます。そのため FLOCSS や BEM などの命名規則を使って今までは無理くり解決はしてきましたが、命名規則地獄は引き続き残ります。また、知らない人にとっては全く意味のわからない規則を他メンバーも覚える必要もあり、あまりチームでの作業には向いていないと思います。(今まで散々やってきましたが・・・)

インラインスタイル

HTML の style 属性にスタイルを書いていくやり方です。このやり方は個人的にはあまりおすすめしません。というのは CSS でシンプルに :hover などの擬似要素やメディアクエリを書くことができないからです。これは UI をスタイリングする上では致命的です。ただ JS の中にスタイルを書くため、動的にスタイルを変更しやすいというのはメリットです。

CSS Modules

先ほどのクラスを使ったやり方で命名規則の問題を書きましたが、その問題を解決してくれるのがこの CSS Modules です。CSS Modules ではクラス名をハッシュ化することで CSS でローカルスコープを実現しています。なので上書きされる必要もなく、命名に悩む必要も無くなります。
今まで通り SASS を使ったりすることもできるので、今までの問題も解決してくれつつ従来のやり方に近い感じで書くことができます。

ただ、デメリットとしては、従来のやり方に近いので、外部 (S)CSS ファイルは別に用意する必要があります。なのでコンポーネントの DOM 構造から振る舞い、そしてスタイルまでを 1ファイル完結することはできません。また JS と CSS なので変数などの共有ができません。色情報やメディアクエリの Breakpoint は共有したい場面が多いのではないでしょうか。その辺が気にならなければ CSS Modules という選択肢も全然ありだと思います。

ちなみに、React では react-css-modules または babel-plugin-react-css-modulesを使うのが一般的のようです。

CSS in JS

先ほどのインラインスタイルは style 属性にスタイルを書く方法に対して、こちらは <style> タグを <head> に動的に挿入してスタイルを適用するやり方になります。なので擬似要素やメディアクエリも書くことができ、JS に書くので変数の共有もできます。もちろん書いた CSS はローカルスコープとなり他のコンポーネントに影響を与えません。今から(やっと)紹介する styled-components も CSS in JS の一種となります。CSS in JS は最近のトレンドで styled-components 以外にも CSS in JS を実現するためのライブラリは多数あります。有名なところだと Radium や Aphrodite などがあります。

styled-components とは

styled-components は React コンポーネントとスタイルをまとめて書くことを目的とした CSS in JS のライブラリです。つまり DOM の構造・振る舞い、そして見た目もまとめて1ファイルで実現することができます。

以下に styled-components の特長を簡単にまとめました。

  • JS(X) ファイル内で css がストレスなく記述できる
    • Sass 的な書き方もできる(ネストや & など)
    • hover などの擬似要素やメディアクエリも書ける
    • Auto Prefixer も自動でやってくれる
    • styled-components が SASS でいう Mixin のライブラリも用意してくれてる
  • JS なので変数の共有ができる
  • Props を使って動的にスタイルを変更しやすい
  • 1ファイルにまとまるので見通しがいい
    • HTML/CSS/JS を行ったり来たりする必要がなくなる
  • React Native にも対応
  • styled-components に対応したライブラリも豊富

現時点では CSS を書く手法としては styled-components が一番注目されているライブラリだと思います。実際に Typetalk の新サイトはこの styled-components を使って全ての UI を作ったわけですが、本当に便利で使いやすく、もう従来のやり方には戻りたくないと思えるくらいです。ぜひマスターしましょう。

では、早速 styled-components を使ってコンポーネントをスタイリングしていきましょう。前述の React Static の導入の項で styled-components を選んだ場合はすでにインストールされています。手動でインストールしたい場合は以下のコマンドでインストールしてください。

styled-components の書き方

先ほど作った MyHeading コンポーネントにスタイルを入れていきましょう。まずは色をつけてフォントサイズを指定してみます。

何行か追加と変更しただけでスタイルが入っているのを確認できると思います。以下からデモを見ることができます。

デモを別ウィンドウで見る

追加したコードを見ていきます。

まずは styled-components を import します。おまじないみたいなものです。

ここでスタイルを定義しています。StyledMyHeading という関数にスタイルの情報と、styled.h1 とすることで DOM に h1 タグが使われるようになります。CSS は ES6 のテンプレートリテラル内に記述します。

最初はこの書き方に戸惑うかも知れませんが、他のライブラリだとプロパティ名をキャメルケースで書かないといけないみたいなルールがあったりするのですが、styled-components は CSS そのままで書けるので、慣れてしまえばストレスフリーです。

先ほどの h1 の代わりに今作った StyledMyHeading を入れることで、スタイルが適用された h1 要素が描画されます。

これが基本的な styled-components の書き方となりますが、実はこのコードはもう少しシンプルに書くことができます。

これでも同じ表示になっていると思います。このコンポーネントの DOM は h1 だけで、それはすでに styled.h1 で定義しているので、再度 DOM ツリーを定義する必要はありません。こちらの方が React も使ってませんし、シンプルでわかりやすいのでこちらの書き方をおすすめします。ただし h1 の中に例えば span タグを入れたい場合など、2つ以上のタグを使う場合はこちらの書き方は使えないので先ほどの書き方で書いてください。

Props を使ってスタイルを変更する

では今度は Props を使い、状態に応じてスタイルを変更してみましょう。
small という Props を作って、StyledMyHeading のフォントサイズを変えてみます。

こちらは JavaScript の三項演算子を使って、small という Props が MyHeading に渡ったらフォントサイズを 28px にするというコードです。下のデモでは MyIntro コンポーネントで使っている MyHeadingsmall Props を渡しているのでフォントサイズが小さくなっていることがわかります。small を外すと元のフォントサイズ 36px に戻ります。

デモを別ウィンドウで見る

続いて Props で文字色も変更できるようにしたいと思います。textColor Props があれば赤色になるようにします。これもやり方は先ほどと一緒です。

デモを別ウィンドウで見る

では、この textColor Props が primary だったら青、secondary だったら赤、それ以外は黒色にしたい場合はどのようにしればいいでしょうか?この場合は JavaScript の switchif else を使えます。その処理を styled.h1 内に書くとごちゃごちゃしてしまうので、そのスタイルの定義は別の関数にまとめた方がわかりやすいです。そんな時に styled-components の css という関数が便利です。css はスタイルを返します。

下のデモは MyIntro 内の MyHeading<MyHeading textColor="primary">...<MyHeading> となっているので青になっているのがわかります。

デモを別ウィンドウで見る

polished

先ほど少し書きましたが、styled-components が便利な Mixin 用のライブラリ polished を用意してくれています。例えば px 単位を rem 単位に変換したい場合は rem 関数が便利です。試しに使ってみます。まずは以下のコマンドで polished をインストールします。

デモを別ウィンドウで見る

このように必要な関数を import して使います。他にも便利な関数が用意されているので使えるものは使いましょう。

injectGlobal

先ほど説明した通り、styled-components の場合は基本 CSS はローカルスコープで書いていく訳ですが、全体のフォントの設定や Reset 用の CSS など、グローバルで使いたい場面も出てきます。そういう時には styled-componentsinjectGlobal を使います。先ほどの React Static を試してみた方は、デフォルトで App.js にその injectGlobal を使って以下のようなスタイルが書かれているのがわかると思います。これは全てのページ(コンポーネント)に適用されます。

injectGlobal を使って先ほどのデモにも bodyfont-family のスタイルを設定しました。

デモを別ウィンドウで見る

まだ他にも ThemeProvider や extend など styled-components について話さないといけないことは多々ありますが、全て書くと長くなり過ぎるので今回はここまでにしておきます。もっと詳しく知りたい方はこちらも公式ドキュメントを読んでみていろいろと試してみてください。

コンポーネント設計

styled-components などこれまでに書いたやり方を使って UI パーツのコンポーネントを作り、そのパーツを積み木のように組み合わせて最終的にページしていくことになるのですが、何も考えずどんどん作っていくってコンポーネントの数が多くなると「これの親コンポーネントはどれだっけ?」とか、どのコンポーネントがどこに紐づいてるのかがわからなくなり破綻する可能性が出てきます。せっかくコンポーネント指向で進めようとしているのに破綻してしまっては元も子もないですね。なのでコンポーネント設計は非常に重要になります。今回の Typetalk サイトでは Atomic Design の概念を導入しました。

Atomic Design とは

Atomic Design についてはすでに詳しく書いている記事が多数あるので、ここで詳しく書く必要はないかと思います。簡単に説明すると、コンポーネントを細かく分類して、そのコンポーネントを組み合わせてデザインを表現する設計です。なので今回のやり方と非常に相性がいいです。

Atomic Design ではコンポーネントを以下の5つの粒度で分類します。

  • Atoms(原子): UI の最小要素
    • これ以上分解できない要素(目安としては1つの html タグで書けるようなもの)
  • Molecules(分子): 2つ以上の Atom をもつ要素
  • Organisms(有機体): Molecules と Molecules/Atoms が組み合わさった要素
  • templates: Organisms/Molecules/Atoms が組み合わさった要素
    • ワイヤーフレーム的な役割
  • Pages: Templates にコンテンツを入れたもの
    • UI 的にはここが完成版となる

最初の方に書いたディレクトリ構成に Atomic Design を導入すると以下のようになります。

先ほど React Static のデフォルトの構成は containers ディレクトリにページのコードを書いていると話しましたが、この構成でいくと components ディレクトリでそれぞれのコンポーネントを Atomic Design に沿って作っていき、最終形となる Pages のコンポーネントを containers ディレクトリ内のコードに import するというだけというようになります。

例えば containers/Home.jspages/TopPage を import するだけのシンプルなコードになります。

これの何が嬉しいかというと、この containersUI 側とロジック側の実装の合流地点になるので、基本的にはそれぞれの実装を壊すことなく作業ができます。今までクラス名を変えただけで実装が動くなって怒られたりしたこともありましたが(逆も然り)、そういうことは基本起こらなくなりますので安心して各々の作業に集中できますね。

また、今回 Atomic Design を導入したことで、UI パーツの再利用化が非常にしやすくなり、見通しもよくなりました。今まで CSS 設計で本当無理やりやっていた概念がここでやっと実現できた気がします。ただ Atomic Design を導入して進めていく中でハマった点や悩んだ点がいくつかあったので共有したいと思います。

Atomic Design でハマった点

最初(Atoms)が辛い問題

まず最初に Atoms の部品(ボタンやリストなど)をひたすら書いていくことになるのですが、Atom などの小さいコンポーネントだと確認がしにくいです。なので出来上がってくるものが見えにくくなり一番しんどい期間となりがちです。

それを解決する為に Storybook という便利な UI テストツールを導入することをお勧めします。ここでは説明しませんが、個人的には React を使ってコンポーネント指向でやる場合は必須のツールだと思っています。基本 Storybook でひとつひとつのコンポーネントの見え方を確認しながら作業できるのでこの問題は解決しました。また、ロジック側の担当との作業の受け渡しも Storybook 上で基本的に行います。これはまた別の記事で説明することにします。

Storybook screenshotStorybook を使ってこんな感じで UI 部品の確認ができます。

Molecules/Organisms 分類問題

どの粒度にするべきかの分類に悩むことも多々ありました。特に Molecules/Organisms の分類が曖昧で悩むポイントではないかと思います。なぜなら Atomic Design のルール上、Oragnisms は必ず Molecules を持たなくてもいいからです。Atoms だけで構成された Organism も存在できます。

ここは特に答えがないので、チームでルールを決めるのが一番いい解決法です。私たちの場合は以下のように決めました。

  • Molecules: 同一ページで2つ以上利用する可能性があるもの
  • Organisms: 同一ページで1つしか使わないもの

例えば Molecules の代表だと複数のボタンをまとめてレイアウトする目的で作る ButtonGroup などでしょうか。こちらは同一ページで何度か使う可能性がありますよね?それに対して、Organisms の代表の Header などは同一ページで2回も使わないですよね?そういうルールを決めることで悩んだ際の指針にすることができます。

コンポーネント増えすぎ問題

Modal というコンポーネントがあったとして、この Modal 用の Heading を作らないといけない場合どうしたらいいでしょうか?まずは ModalHeading コンポーネントを作るやり方が思い付きますが、そういうやり方を繰り返していると特定のコンポーネントしか使わないコンポーネントが増えてしまい、結果破綻してしまう可能性も出てきます。

そこで私たちはこの記事に書いてあるような方法を導入しました。この方法は BEM の考え方と似ています。BEM だと .modal という Block が .modal__heading という Element を持っているのと同じで、Modal コンポーネント(Block)が Modal.Heading という Element を持つようなやり方です。そうすることで Modal 用の Heading ということがわかりやすくなります。

構造的には以下のようになります。

実際にこの構造を取り入れると以下のデモのようなコードになります。

デモを別ウィンドウで見る

この方法でコンポーネントの数も最小限に抑えることができました。

まとめ

さらっと説明するつもりが結局すごく長い記事になってしまいましたが、この記事が以前の僕のように jQuery しか触ったことのないような人達のきっかけになれると幸いです。今後さらに React などのフロントエンドライブラリを使う案件がもっと増えてくると思いますので、これからもフロントエンド業界を生き抜く為には絶対覚えておきたい技術です。その実験場として React Static はとてもいい環境なのでぜひやってみてください。


ヌーラボでは React や Vue.js を使った UI のビジュアル、インタラクションの開発に興味のあるフロントエンドエンジニア(フロントエンドデザイナー)を募集しています

開発メンバー募集中

より良いチームワークを生み出す

チームの創造力を高めるコラボレーションツール

製品をみる