Professional Documents
Culture Documents
大岡由佳 著
くるみ割り書房
第2版まえがき
第 2 版まえがき
ャインシティで開催された、国内最大の技術同人誌即売イベント「技術書典5」にて、初めて世に出
トエンドの一技術をテーマにした本で、内心「大量に売れ残ったらどうしよう」とびくびくしながら
初公開から五ヶ月目にして通算1千部を達成する運びとなりました。今の時代、技術書は商業誌でも
初版は1千部∼3千部という状況の中、これは同人誌の世界ではかなり希な部類に入る成功例だそう
です。
著しく、数ヶ月前の情報がすぐに陳腐化してしまうという状況と、Web フロントエンドの開発者へ
の TypeScript の急速な普及というトレンドの中、マーケットにおける空白をタイミングよく突くこと
ができたのも勝因のひとつだったでしょう。また技術書にはめずらしい、登場人物による会話での説
明が読者に好意的に受け入れられたのも幸運でした。なお読者の方々からいただいた反響・感想のツ
込めなかったのですが、読者からいただいた声の中には「ぜひ改訂版か続編で書いてほしい」といっ
たものがありました。それが今回、第2版を出そうと考えた当初の動機でした。しかしそれに加えて、
初版で説明した技術に関してだけでも、その頒布から半年もたたない間にこれだけの事件が起きてし
まいました。
*1 https://oukayuka.booth.pm/
2
・ React Conf 2018 基調講演での Hooks の発表とα版リリース。2019 年 3 月にリリースされた
create-react-app-typescript は非推奨に。
る。
的となる。
式にリリースされている見通し。
・ React 公式のドキュメントおよびチュートリアルの大部分が日本語化される。
あらためて、Web フロンドエンド業界における技術の進化のスピードにおののいています。特に
ウンスされたことはショックでした。そういった事情のため、今回の改版に当たって内容をかなり大
幅に書き直しています。くわしくは「初版からの差分について」の章をご覧ください。それにしても
React 周辺の技術革新のスピード感、執筆スパンが一年単位と言われる商業誌ではフォローするのが
ほぼ不可能なのではないかと思うほどです。
改版作業は当初見積もっていた分量を大幅に上回り、かなり難航しました。しかしそのおかげで、
を学びたいと考えている方など、多くの方々に読んでいただきたいと考えています。
3
初版まえがき
初版まえがき
学ぶ、学び始めたけれどなかなか理解が進まなかった、また途中で挫折してしまった、そんな人たち
に向けたまじめな入門書です。
JavaScript ライブラリです。その登場はたちまちフロントエンド開発者たちの注目を集め、あっとい
う間にその分野でのデファクトスタンダードともいえる地位に上り詰めました。
4
npm のダウンロード数の統計では、翌年の 2016 年には早々に jQuery を抜き、2018 年 10 月の現在
中には根本から作り直してほぼ別物になってしまったプロダクトもあるほどです。
その React 登場当時の衝撃と興奮を、残念ながら著者はリアルタイムでは体験していません。その
ときはフロントエンドにあまり興味がなかったせいもありますが、まだドキュメントが少なく周辺情
報も不足しており、かみくだいて解説してくれる人もあまりいない状況でしたので、著者の頭では「な
んだか難しそう。JavaScript でそんなに複雑なことする必要あるの?」と思うだけでした。そのすご
さが理解できなかったのです。
Native が登場して盛り上がりを見せ、そのシーンが無視できなくなってきたため、私も重い腰を上げ
また「なぜこれをするのにこんなに回りくどいことを書く必要があるのだろう?」と疑問に思う点も
いくつかあり、なかなか先に進めないでいました。
いるのをよく見かけたのですが、そんなときは文系出身エンジニアの著者とは頭の出来が違うのだろ
その後、フロントエンドエンジニアとして3つの現場を経験してチームを統括したり、経験の浅い他
い」と感じたか、そしてガチ勢の人たちが「React は簡単」と自慢(?)のように言っていたのかが
よくわかります。
React はごくシンプルな思想のもと、恐ろしく複雑なシステムでも大きな破綻がなく信頼性の高い
5
初版まえがき
アプリケーションを作ることを可能にする技術です。そのシンプルな思想を貫徹しようとして、とき
には回りくどい書き方を開発者に強要してきますが、それはとっつきやすさを犠牲にしてでもコード
な迷走をしたのでした。
「simple」と「easy」
、日本語ではよく混同して使われることもありますが、似て異なる概念です。現
は「easy」なフレームワークと言えるでしょう。入門者のことを考慮して敷居が下げられていて、段
階的に複雑なやり方にステップアップできるような作りになっています。しかしそれは同じことを実
現するのに唯一の正解というものがなく様々なやり方が許容されており、原理の一貫性に欠けるとい
う側面を併せ持ちます。
向データフローといった原理が一貫しており、それを遵守するために一見回りくどくコードの記述量
が増えるやり方を要求されることも多々ありますが、React で正しく開発したアプリケーションは非
常に複雑なシステムでも見通しがよくて読みやすく、テストやデバッグがしやすく、メンテナンス性
や拡張性が高いものになります。
ただその原理こそシンプルですが、そこに高い理解度と応用力が求められるため、React は学習コ
ストが低いとは決して言えません。またそれらの原理だけを抜き出して説明されても、初学者にはま
ったくピンとこないでしょう。でもそれを使う必要にせまられたとき、その背景にある思想やメリッ
トを噛み砕いて説明してくれて、初学者が抱きがちな疑問を解消してくれる、都合のいい話ですがそ
機会はありません。だから著者はこの本をふたりの、経験者のシニアエンジニアと初学者の駆け出し
エンジニアによる対話形式で書こうと思いたちました。
あの、自分の理解が煮詰まっていたときにこの説明を誰かがしてくれていたら、すんなり理解でき
ていたはずなのに……。著者自身のそんなありえない後出しの願望を架空のキャラによって満たすこ
考えました。
「React は難しくて勉強するのがつらい」
「Redux は回りくどくてその必要性がわからな
い」
「JSX がキモいから React に抵抗がある」等々、そんな愚痴や不満を口にしている方々にぜひ読ん
6
でいただきたい。そして React への(著者から見れば)不当な評価を覆してぜひ React を愛するよう
になってほしい、そんな目論見を抱きつつ本書を執筆しました。
対話ベースのペアプログラミングによる生産性の向上は数々の論文で報告されていますし、Ruby
の作者まつもとゆきひろ氏も、テディベアプログラミングのようにあえて誰かに説明する体裁をとる
ことで開発者はより生産的になれると述べています。軽い気持ちから始めた対話体による執筆でした
が、こうして自分で読み直してみても、その試みは成功しているように思えます。
(※自画自賛は著者
の性格なので、ご容赦ください)
とを目的として構成されており、内容をそのために必要な情報量に絞り込んでいます。ゼロから趣味
のアプリを作り上げられるようになりたい、といった方には向いていないかもしれません。また文中
に示される、特定技術や思想への見解・感想はあくまで著者個人の考えであることも併せてご理解願
います。
サンプルコードが置いてある場所は https://github.com/oukayuka/ReactBeginnersBook-2.0
*2
です。本文中で随時、個別のファイルを引用していますが、ページの都合上ところどころ、説明の本
筋とは関係ない部分を断りなく省略してあるなど完全に同じではありませんので、ご承知おきくださ
い。
*2 初版のサンプルコードは https://github.com/oukayuka/ReactBeginnersBook
7
本書について
本書について
登場人物
柴崎雪菜(しばさき・ゆきな)
とある都内のインターネットサービスを運営する会社のフロントエンドエンジニア。React 歴は 2 年
半ほど。本格的なフロントエンド開発チームを作るための中核的人材として、今の会社に転職してき
た。チームメンバーを集めるため採用にも関わり自ら面接も行っていたが、彼女の要求基準の高さも
アを回してほしい」と上層部に要望を出し、社内公募が実行された。
秋谷香苗(あきや・かなえ)
柴咲と同じ会社のエンジニアで新卒二年目。入社以来もっぱらサーバーサイド開発に携わっていたが、
柴崎のメンバー募集に志願してフロントエンド開発チームに参加した。そこで柴崎から「一週間で戦
力になって」と言われ、マンツーマンで教えを請うことになるが……。
8
初版からの差分について
初版からの差分について
初版から本書第2版への変更部分については以下の通りです。
ー ジ そ の も の が 非 推 奨 に なっ た た め 、 create-react-app-typescript を 使 用 して い た 部 分 を
create-react-app に入れ替えた。
・ 「2-5. 便利な配列やオブジェクのトリテラル」に、分割代入についての説明を追加。
・ 「3-5. クロージャ」の章を追加。
・ 「3-6. ジェネレータ」の章を追加。
・ 「4-5. コンパイル設定」に、絶対パスインポートについての説明を追加。
い将来に非推奨になるとアナウンスがあったため、
「6-1. TSLint」の内容を「6-2. ESLint」に変
Component(FC)に変更。
Recompose」から「Hooks で関数コンポーネントを強化する」に置き換える形で刷新。
に切り替えた。
FSA を使わない手法を使ったものに書き換えた。
9
本書について
委譲。
・ 「第 11 章 Redux で非同期処理を扱う」の内容を追加。
・ その他使用している主なソフトウェアを、2019 年 3 月時点の最新バージョンにアップデート。
10
本文中で使用している主なソフトウェアのバージョン
本文中で使用している主なソフトウェアのバージョン
・ React 16.8.6
・ TypeScript 3.4.3
・ react-router-dom 5.0.0
・ Redux 4.0.1
・ Redux-Saga 1.0.2
11
目次
目次
第 2 版まえがき ……………………………………………………………………2
初版まえがき ………………………………………………………………………4
本書について ………………………………………………………………………8
登場人物 …………………………………………………………………………………………………………8
初版からの差分について ………………………………………………………………………………………9
本文中で使用している主なソフトウェアのバージョン ……………………………………………………11
目次 …………………………………………………………………………………12
プロローグ …………………………………………………………………………15
第 3 章 関数型プログラミングでいこう ………………………………………37
3-1. 関数型プログラミングは何がうれしい? ……………………………………………………………37
12
3-2. コレクションの反復処理 ………………………………………………………………………………38
第 7 章 何はなくともコンポーネント …………………………………………77
7-1. React の基本思想 …………………………………………………………………………………………77
13
目次
エピローグ ………………………………………………………………………185
あとがき …………………………………………………………………………187
著者紹介 …………………………………………………………………………189
14
プロローグ
「おはようございます、芝崎さん。本日からお世話になります、秋谷香苗です!」
「はい、秋谷さんね。こちらこそよろしくお願いします。私はこのフロントエンドチームの、って言
っても今のところは私と秋谷さんとのふたりだけなんだけど、リーダーの柴崎雪菜です」
「柴咲さんのこと、私知ってましたよ。芝崎さん、転職してきて間もないけどできる女性エンジニア
柴咲さんに教わっていっしょに働けるチャンスだっていうので、社内公募に応募したんです」
「……そ、そう。ありがとう。やる気は十分ってことですね。じゃあ今から、私があなたに何を期待
しているか、何をやってもらうかを説明していこうと思うけど、いいですか?」
「はい、よろしくお願いします!」
「その前に、秋谷さんはいま入社何年目? あと持ってるスキルについても教えてくれるかな」
た」
「うん、わかりました。React については?」
「興味があったので自分で勉強しようとしたんですけど、
途中で難しくて挫折しちゃいました……。Vue
した。すみません……」
「MVVM ?」
「Model、View、ViewModel からなる構成、といっても説明が長くなるので今は省くけど、HTML
クと共通してるよね」
「はい、私もそう思いました」
15
プロローグ
「React の設計パターンはそもそもパラダイムが違うから、その思想を理解しないまま飛び込んでも
なかなか身につかないんだよね。でもそれらは随時、説明していくので心配しないで」
「はい! ありがとうございます!」
「あはは、いい返事だね。で、これから何をやってもらうかだけど。今から私がマンツーマンでつい
てもらえるレベルになってもらいたいかな」
「えっ、たったの一週間でですか?! ムリムリ、実質五日間しかないじゃないですか?!」
「いや、それだけあれば十分でしょう。私、教えるのうまいので」
「……ええええ? 不安だなぁ」
は難しくないから」
「……わかりました! 柴咲さんがそう言われるなら、覚悟を決めてがんばります!!」
16
1-1. 環境構築
第 1 章 こんにちは React
1-1. 環境構築
ェクトごとに違ったバージョンを共存させることが必要になることがあります」
「はい」
ととかがその理由かな」
「
『anyenv ndenv install』とかで検索すれば、インストール方法は見つかるから」
「だいじょうぶです! すぐやります!」
―― 5分後 ――
「柴崎さん、できました!」
「……実行、と。それぞれ『v11.10.0』
『6.8.0』が表示されました」
「あの、あまりわかってないんですけど、Yarn って何ですか?」
モジュールパッケージの追加・削除に加えて、各パッケージ間のバージョン整合とかも自動的にやっ
17
第1章 こんにちはReact
うで、ウチでも使ってるの。さっき打ってもらったのは、Yarn をグローバル環境にインストールする
コマンドだね」
「次はエディタね。秋谷さんは今、何のエディタ使ってる?」
って呼ぶけど、それを使ってもらいます」
「えっ、チームでエディタを指定されるんですか?」
「本当は各自好きなものを使っていいよと言いたいんだけど、これから入ってくるであろうメンバー
のチェックとかを自動的にやってくれること。あとプラグインが豊富で、コードの自動整形とかも簡
単にできる。
理由のもうひとつは、実際の開発は常にペアプログラミングでやってもらうんだけど、ひとつの画面
れぞれ自分のマシンで同時コーディングをしていく予定だから」
「同時コーディング?」
と同じようなことがコーディングでできるようになるの」
「へー、今はそんなことができるようになってるんですね。近未来だ……。わかりました、がんばっ
グでコードが書けるようになるよ」
「あ、それ嬉しいです」
これ以降のサンプルコードにも置いておくので、一度は気にして見ておいて」
18
1-2. Hello, World!
「了解です!!」
マージしたりもできる。
・ indent-rainbow …… インデントの階層を色分けして見やすくしてくれる。
る。
「はい、じゃあ次はお約束の『Hello, World!』をやってもらいましょうか」
ークではなく、単なる UI ライブラリだ』って言ってるくらいだし。一昔前なら、適当なボイラープ
19
第1章 こんにちはReact
App っていう、そのまんまな名前のコマンドモジュールを出してくれているので、それを使わせても
らいます」
「よかった。ちゃんとあるんですね」
ね」
「なるほど。……はい、実行完了しました」
ウザが開いてその画面を表示するようになってるの」
「なんか文章も書いてありますね。
『Edit src/App.tsx and save to reload.』だそうです。このファイ
ルを開きますか?」
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
</body>
</html>
20
1-2. Hello, World!
体はどこにあるんでしょうか?」
すね」
21
第1章 こんにちはReact
「????」
「まあ、いきなりじゃわかんないだろうね。順番に説明していくからしっかり聞いてて。
まず React で作られるアプリケーションは、すべてコンポーネントの組み合わせで構成されるの。
HTML タグがそのまま書ける拡張記法を使うのよ。
てるわけですね」
結びつけてくれてる。まあこのあたりは私もふだん忘れてるところなので、そういうもんなんだって
くらいの理解でいいよ。そんなにいじらないとこだし」
「わかりました」
「じゃ、src/App.tsx をこんな感じに変更してから保存してみてくれる?」
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
- Edit <code>src/App.tsx</code> and save to reload.
+ Hello, World!
</p>
「あっ、勝手にブラウザがリロードされて、ページの内容が『Hello, World!』に書き換わりました」
22
1-3. Yarnコマンド
まだいろいろ納得はいってないだろうけど、これでいちおう『Hello, World!』はこれで完成」
「そうですね。あまり自信はないですけど、とりあえず手順はおぼえましたし、雰囲気はわかったの
でよしとします」
「うん。細かいところについては、おいおい理解していけばいいよ」
「さっき生成したコードをリポジトリに上げたものがあるので*1、ちょっと別のディレクトリに落とし
てきてくれる?」
「……はい、ローカルに展開しました」
「じゃ、このアプリを起動してみて」
*1 https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/01-hello/03-hello
23
第1章 こんにちはReact
「うん、ディレクトリを見てみて。node_modules/ ディレクトリがないでしょ。パッケージモジュール
にアプリが立ち上がりました」
コマンドだけど、メインの役割はパッケージモジュールの管理なので、そこから説明していこう。と
りあえずよく使うのはこれくらいかな」
れる。ちょっとファイルを開いてみて」
{
"name": "hello-world",
"version": "0.1.0",
"private": true,
"dependencies": {
"@types/jest": "24.0.6",
"@types/node": "11.9.4",
"@types/react": "16.8.4",
"@types/react-dom": "16.8.2",
"react": "^16.8.3",
"react-dom": "^16.8.3",
"react-scripts": "2.1.5",
"typescript": "3.3.3333"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
24
1-3. Yarnコマンド
「dependencies の中にパッケージらしき名前とバージョン番号がありますね。これのことですか?」
こうか」
「はい、お願いします!」
スの解決を自動で行ってくれるのでコマンドは直に書けるけどね」
「えー、そうだったんですか。じゃ、ここに自分でエントリを追加すれば、yarn コマンドで実行でき
るんですか?」
「そうだね。後々、構文チェックのためのコマンドのエントリとかを追加していく予定。その他、ド
ドの実行に割り込んで起動させたりもできるよ」
「へえ――、便利ですね」
ックで該当ファイルが開くからね」
「なるほど、ありがとうございます!」
25
第2章 ナウでモダンなJavaScript
第 2 章 ナウでモダンな JavaScript
2-1. ECMAScript
いて学んでおいてもらう必要がありそう。秋谷さんは、JavaScrpt はさわったことはあるんだよね?」
することがありましたので」
「いーえすご?」
「正式には『ECMAScript 5』
。ECMAScript(エクマスクリプト)は JavaScript の標準仕様で、近年
語と思ってもらってもいいくらい」
「……うっ、そうなんですね」
「はい、お願いします!」
26
2-2. 変数の宣言
2-2. 変数の宣言
使ってはいけません」
「え、絶対にダメなんですか?」
「もちろん。じゃ、とりあえずこのコードを見てみて。結果はどうなると思う?」
var n = 0;
if (true) {
var n = 50;
var m = 100;
console.log(n);
}
console.log(n);
console.log(m);
「var で定義された変数のスコープってね、関数単位なんだよ。だから制御構文のブロックをすり抜
けてしまう。Ruby を始めとする大多数の言語の変数スコープはブロック単位だよね。その考え方に
「素直でいいね。じゃ、次行ってみようか」
27
第2章 ナウでモダンなJavaScript
2-3. アロー関数
を使うのが普通でしたから」
「みたいだねー。知ってるなら話が早い。ES6 で導入されたアロー関数リテラルというのがあるんだ
けど、これは実際に見てもらったほうが早いね」
// 従 来 の 関 数 宣 言
function plusOne(n) {
return n + 1;
}
// ア ロ ー 関 数 の 宣 言 そ の 1
const plusOne = (n) => { return n + 1; };
// ア ロ ー 関 数 の 宣 言 そ の 2
const plusOne = n => n + 1;
に似てますね」
「アロー関数式の『その1』と『その2』も比べてみて。引数がひとつだけの場合はカッコが省略で
「へー、こっちのほうがスッキリ見やすくていいですね」
「だから省略できるときは省略しましょう。ただし、function での関数宣言とアロー関数式での宣言
は this の挙動が変わるので気をつけてね。その違いを理解するために、次のコードを実行してみて」
const obj1 = {
num: 444,
28
2-3. アロー関数
fn: function() {
console.log(this.num);
}
};
const obj2 = {
num: 888,
fn: () => {
console.log(this.num);
}
};
obj1.fn(); // 444
obj2.fn(); // undefined
「……えっ? これってどういうことですか?」
関数それ自体になるの」
「うーん、わかったようなわからないような……」
「この例だけではピンと来ないかもね。関数に引数として関数を受け渡したりすると、この違いを意
識してないと思った挙動と異なることが出てきたりするんだけど、今の段階では知識として知ってお
いてくれればいいから」
「はい。じゃ、とりあえずおぼえておきます」
みかなとも思うんだけど、this の挙動を統一する必要もあるし、どうしても仕方がないとき以外、極
力アロー関数を使うようにしましょう。見た目もいかにも関数型っぽくてイケてるし」
「カッコよく書けたほうがモチベも上がりますしね」
「お、秋谷さんもそう思う? ちなみにあとでコーディングルールをユーザーに強制するツールを紹
介するんだけど、私のカスタマイズ設定ではアロー関数で書かないと怒られるように専用のプラグイ
ン を組み込んでルールを設定してます」
*1
「あはは、徹底してますね……」
*1 eslint-plugin-prefer-arrow https://github.com/TristonJ/eslint-plugin-prefer-arrow
29
第2章 ナウでモダンなJavaScript
「あと、これはアロー関数に限ったことじゃないけど、ES2015 から関数のデフォルト引数が使える
ようになったので、補足しておくね」
console.log(plusOne(5)); // 6
console.log(plusOne()); // 1
JavaScript では使えなかったんですか。知らなかった……」
「まあ今やたいていの言語にはそなわってる機能だからね。使えるときは積極的に使っていこう」
2-4. クラス構文
にもクラスが使えるようになりました」
「へー、そうなんですね。JavaScript は普通にクラス使えなくて、プロトタイプ?とかいう仕組みを使
って擬似的にやる必要があるって聞いてました」
「ES5 まではそうだったね。とりあえずサンプルコードを見てもらおうかな」
class Bird {
constructor(name) {
this.name = name;
}
chirp() {
console.log(`${this.name} が 鳴 き ま し た `);
}
static explain(name) {
30
2-5. 便利な配列やオブジェクトのリテラル
console.log(`${name} は 翼 が あ っ て 卵 を 生 み ま す `);
}
}
fly() {
console.log(`${this.name} が 飛 び ま し た `);
}
}
「素直な構文ですね。これといって不明なところはなさそうです!」
的に書く必要があることくらいかな。サンプルコードにはついでにテンプレートリテラルをさらっと
差し込んでおいたんだけど、わかった?」
「文字列を通常のシングルやダブルのクォートじゃなくバッククォートで囲んだ上で、変数名を ${}
2-5. 便利な配列やオブジェクトのリテラル
「はい。それじゃ配列やオブジェクトの便利な書き方を見ていこうか。まずは分割代入からね」
31
第2章 ナウでモダンなJavaScript
obj.name; みたいに書くと怒られるの」
「へー、そんなになんですか」
「うん。だから積極的に使っていきましょう。
それから次はコレクションの展開構文。これも口で説明するより、コードで見てもらったほうが早
そう」
const obj1 = { a: 1, b: 2, c: 3 };
const obj2 = { ...obj1, d: 4, e: 5 };
console.log(obj2); // { a: 1, b: 2, c: 3, d: 4, e: 5 }
「なるほどー、配列やオブジェクトの名前の前に『...』をつけることで中身が展開されるんですね。
これは便利そう」
「この書き方をスプレッド構文と言って、
『...』をスプレッド演算子と呼ぶの。ちなみに配列のスプ
ぼえといて。
それじゃ、次はこんな構文ね」
32
2-6. 非同期処理を扱う
ェクトキーに、変数値がその値になってるわけですか」
「その通り。これはプロパティ名のショートハンドとかって呼ばれる書き方。ES6 から導入された構
「へぇ――。こういった書き方、知らずに見てたら混乱したかもしれないですけど、見た目もスッキ
リするしいいですね。私も使いこなしていきたいです!」
2-6. 非同期処理を扱う
てそこから得た値を加工して表示、みたいな処理は何の気なしに続けて順番に書いてただろうけど、
JavaScript で考えなく同じことをやろうとすると、リクエストの結果が返ってくる前に加工処理に進ん
で表示されたりしてしまいます」
「えっ? それ困ります」
「だよねー。ちょっと次のコードを実行してみて」
33
第2章 ナウでモダンなJavaScript
console.log(' お は よ う ! ');
}
greet();
「
『おやすみ』と『おはよう!』が即座に表示されて、しばらくしてから『起きた』が表示されまし
た」
「setTimeout() は指定した時間だけ遅延して任意の関数を実行するものね。ここではわかりやすくこ
れを使ったけど、JavaScript では通信とかローカルファイルの読み込みとかの外部アクセス処理は、ほ
ぼ断りなく非同期が前提になってるからね」
「え――っ、そうなんですか?」
「React コンポーネントのレンダリングだってそうだよ。上の階層からとか関係なく、前処理が終わ
ったものから他を待たずに表示されていくの」
る Rails とは全然ちがいますね」
「いいところに気がついたねー。その話題は後にとっておくとして、非同期処理のプロセスを待って
sleep(2000)
.then(() => {
console.log(' 起 き た ');
console.log(' お は よ う ! ');
})
.catch(err => {
console.error(' 睡 眠 例 外 で す : ', err);
})
}
greet();
「今度は『おやすみ』が表示された後、しばらくして『起きた』
『おはよう!』と表示されました」
34
2-6. 非同期処理を扱う
つなぐことで、非同期処理をひとつひとつ処理が終わるのを待って順番に実行していくことができる
ようになったよ」
「うーん、でもあまり読みやすい構文じゃないですよね」
されたわけなんだけど、こっちのコードも見てくれる?」
try {
await sleep(2000);
console.log(' 起 き た ');
console.log(' お は よ う ! ');
} catch (err) {
console.error(' 睡 眠 例 外 で す : ', err);
}
}
greet();
「実行結果は同じですけど、なんかすっきりと見やすくなりましたね」
果を待ってくれるようになるの」
*3「エイシンク・アウェイト」と読む。
「asynchronous(非同期な)
」と「await(∼を待ち構えてい
る)
」から。「アシンク」ではないので注意。
35
第2章 ナウでモダンなJavaScript
トを返してるの」
「はー、なるほど」
「一般的なプログラマにはこっちの書き方のほうが直感的だし、コールバック関数が好きな人ってあ
まりいないんじゃないかな。余計な階層が増えるし。だから非同期処理はどうしても仕方がない場合
「わかりました! こっちのほうが直感的というのは、私も同感です」
*4 「糖衣構文」とも。プログラミング言語において、読み書きのしやすさのために導入される書き方で、複雑でわ
かりにくい書き方と全く同じ処理になる文を、よりシンプルでわかりやすい記述法で書くことができるもののこと。
36
3-1. 関数型プログラミングは何がうれしい?
第 3 章 関数型プログラミングでいこう
3-1. 関数型プログラミングは何がうれしい?
「はーい、次はお待ちかねの関数型プログラミング講座です」
「ううっ、敷居高そう……」
「まあまあ、そう難しく考えないで。React による開発ではオブジェクト指向の出番はあまりなくて、
タラクティブな操作という入り組んだ超複雑な難問に立ち向かうため、関数型のアプローチが最適だ
と Facebook の開発者たちが考えたからだね」
「どうして関数型のアプローチだと有効なんですか?」
「クラスから生成されたオブジェクトは内部に状態を抱えていて、それによって振る舞いが変わって
しまうでしょう。具体的にはメンバー関数の実行結果がメンバー変数に依存する。つまり副作用が大
きくて予測がつきづらい」
「うーん、私はそういうもんだと思ってきたので、それが問題だと言われてもピンと来ません……」
「Rails では処理はあくまで同期的だし、そこまで複雑なことはしないからね。でも非同期処理が関
わってくると、その不確実性は人間の頭で予測するのはほぼ不可能なレベルになる。別のプロセスか
らの処理がそのオブジェクトの状態を変えてしまっていたら、想定したのと違う振る舞いが起きかね
ない」
「……それはそうですね」
らね」
「そうでした」
「だからこそ状態を内包してしまうクラスを用いた書き方は、どうしても使う必要があるところだけ
に限定しておいたほうがいいの。その必然性は、React の学習が進むにつれて実感してくるでしょう。
ところでここまで『関数型』って何気なく言ってきたけど、秋谷さんは『関数型プログラミング』
ってどういうことか理解してる?」
37
第3章 関数型プログラミングでいこう
「ほ――、なんとなくわかってるじゃない。
『関数』っていうのは、数学の関数と同じものなんだよ。y =
入力に対して同じ作用と同じ出力が保証されていることを参照透過性っていうんだけど、そんなふう
「なるほどー。でもどうしても状態を抱えることが必要な場面ってありますよね? たとえばユーザ
ーのログイン状態とか。そういうのって関数型で処理できるんですか?」
「おっ、鋭いところをついてくるねー。それに関してはもっと後で説明することになるんだけど、ち
ょっとだけ触れておくと、React でよく用いられるのは状態を引数にとって新しい状態を返すやり方
ね。そして別の引数に対して、返される状態の差分が常に一定であることが保証されればいいわけ。
そうやって状態も外部データとして扱い、振る舞いを分離させて副作用を抑えるのが関数型のやり方
と言えるかな」
「ん―――?」
「まあ具体的なコードがないとピンとこないだろうから、先に進みましょう。さっきあなたが挙げて
3-2. コレクションの反復処理
「コレクションの反復処理構文にいってみようか。これもまずサンプルコードを見てもらったほうが
いいね」
38
3-3. 関数型プログラミングの概要
・ map() は対象の配列の要素一つ一つを加工した新しい配列を作る
・ filter() は条件に適合する要素だけを抽出して新しい配列を作る
・ every() はすべての要素が条件を満たすかを真偽値で返す
・ some() は条件を満たす要素がひとつでもあるかを真偽値で返す
・ includes() は指定した要素が含まれるかを真偽値で返す
・ reduce() は配列の要素を、与えた式で畳み込んだ値を返す
・ sort() は各要素を、与えた条件によって並び替えた新しい配列を返す
「一気に説明するとこんな感じかな。だいじょうぶそう?」
これらが関数型プログラミングで多用されるのは、非破壊的な処理を行ってくれるから。map() や
filter() は元の配列の値を一切いじらずに、新しい配列を生成して返すよね。関数型プログラミング
は副作用を嫌うと説明したと思うけど、だから相性がいいの」
3-3. 関数型プログラミングの概要
「えっ?」
「だって、これを書き換えるとこうなるんだよ」
39
第3章 関数型プログラミングでいこう
んにはなじみのない手法だと思うけど」
「……言われてみればそうですね。なんとなくわかってた気になってただけかも」
関数を代入してるんだから。そんなことしたことないでしょ?」
「ううっ」
「じゃ、そこも含めてあらためて確認していこうか。一般的に関数型プログラミングでは、だいたい
以下のようなことが普通に行われます」
1. 名前のない使い捨ての関数(無名関数)が使える
2. 変数に関数を代入できる(=変数に無名関数を代入することで名前をつけられる)
3. 関数の引数に関数を渡したり、戻り値として関数を返すことができる(高階関数)
4. 関数に特定の引数を固定した新しい関数を作ることができる(部分適用)
5. 複数の高階関数を合成して 1 つの関数にできる
入することができるというのが2で言ってること」
「ああ、だんだんわかってきました」
「よしよし。3と4はあらためて次で説明してあげるよ。5についてはもっと後じゃないと実例が示
せないから、とりあえず棚上げにしておく」
40
3-4. 高階関数
3-4. 高階関数
「高階関数とは、引数に関数をとったり、戻り値として関数を返したりする関数のこと。英語では
「関数を引数にとる関数…………」
無名関数を引数として渡されてる」
「あっ、そうか」
「戻り値として関数を返す関数、というのはもうちょっと複雑になる。たとえばこのコード」
実行してみて」
「ふふ、わかるけどなんでこんなことするんだろうって、モヤモヤした顔してるね。必要な場面に迫
られて実践を積んでいかないと、この便利さはわからないだろうから、とりあえずやり方だけおぼえ
てくれてればいいよ。
あと return は省けるから、関数 hof は (ex, fn) => n => fn(n + ex) とも書ける。というか省略でき
るものは省略したいので、むしろこっちのほうで書いてほしいかな」
「了解です」
「後のほうで、高階関数の応用としてコンポーネントを引数にとってコンポーネントを戻り値として
機能を付与するためによく使われるテクニックなんだけど、原理はこの高階関数と同じだから、これ
41
第3章 関数型プログラミングでいこう
3-5. クロージャ
「次はクロージャについて」
は Closure。日本語では『関数閉包』と言われる書き方。説明のために、まずはやりたいことをクラ
スを使って書いてみるね」
class Counter {
constructor(initialCount) {
this.c = initialCount;
}
increment() {
return this.c++;
}
}
「実行ごとに値が加算されていくカウンターですね。クラスなら簡単に実現できますけど、これを関
数だけでやれと言われると難しい気がしますね。外にグローバル変数を用意して、それを参照するよ
うにすれば書けなくはないですけど……」
「うん。わかっていると思うけど、グローバル変数に依存した処理を書き散らすのはコードの可読性
を落とし、各モジュールの独立性を損なって予期せぬ挙動を引き起こすので、基本的には禁物だよ。
42
3-5. クロージャ
で、こういう場合にクロージャを使うとうまく書けるの。上の例で実現している機能をクロージャを
使って書くとこうなる」
return increment;
};
になるんでしょうか?」
れているから変数 c の値が毎回リセットされることなく蓄積されていくわけ」
「んん―――、わかったような気もするけど、なんか強引に納得させられたような……」
「それを言うなら、クラスを使った書き方だって同じだと思うよ。要は慣れの問題。自分でいくつか
クロージャを使ったコードを書いてみれば考え方になじめるんじゃないかな。
ちなみにこのサンプルは戻り値として関数を返している高階関数との合わせ技だけど、クロージャ
は必ずしも関数を返す必要はないから、そこは勘違いしないでおいて。あくまでクロージャっていう
のは、単に親関数スコープの変数を参照する関数のことを指すので」
「関数でコンポーネントを実装するときに、その中で外のスコープの変数を参照する関数を定義する
ことはよくあるから、それは結果としてクロージャになるかな。その場合は、まああまりクロージャ
と意識することはないかもしれないけど」
43
第3章 関数型プログラミングでいこう
3-6. ジェネレータ
他にジェネレータ関数というものがあるの。ジェネレータの記述方法はちょっと独特で、function*
サンプルコードを実行してみたほうが早いね」
「return 文がないのに、戻り値は関数を備えたオブジェクトなんですね」
「そこも特殊だよね。戻り値のオブジェクトから next()という関数が実行できるんだけど、それによ
「……見た目もそうですけど、使い方もかなりクセのある機能ですよね。芝崎さんはこれをどんな場
面で使ってるんですか?」
「自分で一から書くときでジェネレータがどうしても必要になった場面は正直なところ、今までない
44
3-7. カリー化と関数の部分適用
きに頻出する記法なので、今のこの段階で教えておきたかったの」
「なるほど。そのときになってあわてないよう、しっかりおぼえておきますね」
3-7. カリー化と関数の部分適用
「ところで秋谷さんはカリー化って言葉、聞いたことある?」
「なにそれおいしそう。コロッケにカレー粉を入れてカレーコロッケにするみたいなことですか?」
「うんうん、模範的な回答をありがとう。確かにスペルは同じ『curry』だけどね。でもこれは人名か
数型プログラミングの一概念にちなまれてる稀有な例なの」
「すごい。リアルで『俺が……、俺こそが関数型プログラミングだ!』みたいな発言が許される人で
すね」
「もう故人だけどね。それでカリー化(currying)とは何かという話に戻るけど、カリー化とは端的
に言うと『複数の引数をとる関数を、ひとつだけ引数をとる関数に分割してネストさせること』なの」
「????」
「コードを参照したほうが早いね」
*1 https://redux-saga.js.org/
45
第3章 関数型プログラミングでいこう
って n と m の積を返す関数』を返す関数……ってことでしょうか?」
「そう、これはさっきやった高階関数だね。こうやって、ひとつずつの値を返す関数がネストした高
階関数にすることが『カリー化』
」
「なるほど、わかってきました。実行時にカッコがふたつ並んでるのは……」
はもうひとつカッコが必要なの」
だけですね」
「そうだね。高階関数の説明のときにも、こっちの書き方をすすめたけど、できるだけシンプルなほ
うで書きましょう。じゃ、次はカリー化された関数の『部分適用』について説明しよう」
けてますね」
「なるほど、こうするとどんな数を渡しても、常に3倍される関数が作れるんですね」
「そう、こんなふうにカリー化された関数の一部の引数を固定して新しい関数を作ることを『関数の
部分適用』っていうの」
46
3-7. カリー化と関数の部分適用
「なるほどー。柴崎さんの説明を聞くまでは、
『関数型プログラミング』って言葉を見ただけで難し
そうって尻込みしてましたけど、こうやって段階を踏んで教えてもらえたおかげで、かなり理解でき
ました。ありがとうございます!」
「うん、でもこれが関数型プログラミングの全てってわけじゃないけどね……。React を使って開発
する上で必要なものに絞って教えたつもり」
「React で開発するときって、高階関数とかよく出てくるんですか?」
りすることはままあるね。設定のための値はほぼ不変なのでそこだけ部分適用した関数を取り回した
り、みたいに使ったり。サンプルをコピペすれば使えなくもないけど、理解して使うのとそうじゃな
いのとでは全然ちがうと思う。
さらに、後のほうでこれもあらためて説明するけど、React を使う上でのテクニックで『コンポー
ネントを引数にとってコンポーネントを返す関数』というものを使う場面が、ちょっと高度なことを
しようとすると出てくるの。それを理解する上で高階関数の概念は知っておいたほうが絶対いいと思
う」
「……そうなんですね。以前の私は、関数型プログラミングへの理解がほとんどないまま突撃したせ
いで玉砕しちゃったのかもしれません。でもこうやって少しずつ理解を積み上げていくと、見える世
界が広がってきて、前はわからなかったことも理解できるような気がします」
「よし。その気持ちを忘れずに、どんどん先に行ってみよう」
47
第4章 型のあるTypeScriptは強い味方
「このチームのメイン開発言語って聞いてます。でも疑問なんですけど、JavaScript を生で使ってる人
「それについての理由はいくつかあるね。でもそれを説明する前に、TypeScript がマイナーというイ
メージを修正してもらおうかな」
「……すいません、マイナーだと思ってましたが違うんですか?」
「えええ、Ruby より上なんですか?!」
「それだけじゃない。成長率ランキングでは、Go に次いで僅差の 2 位」
「私の知らないところでそんなことに……」
「AltJS って昔は色々あったけど最近は聞かないので、すぐ廃れてしまうイメージでした」
48
4-1. TypeScriptは今やメジャー言語
いるの」
「ええー!」
「この結果には私もびっくりしたけど」
「これは世界のトレンドだけど、日本もその例外じゃない。この写真を見て。これは 2018 年 11 月
者が自分の使っている言語にシールを貼っていく投票ボードの途中結果を私が撮影したものなんだけ
ど」
「えっ、TypeScript が圧倒的じゃないですか!」
「カンファレンスに参加するような意識高めの人たちの間での自主投票だから実際の現場ではここま
49
第4章 型のあるTypeScriptは強い味方
日々拡大してる」
「……なるほど、私の認識がまちがってたことはわかりました。でもどうしてですか? TypeScript
が今こんなに使われるようになってるのって」
「これは私の考えだけど、静的型付け、型推論、Null 安全性という最近のプログラミング言語のトレ
「うーん、型ってそんなに必要ですか? 今ひとつピンとこないんですけど……」
「言われてみればそうなんでしょうけど……」
いい手軽さがウケたんだと思う。Java なんか文字列だけで3つくらい型があって、取り扱いの煩雑さ
といったらなかった。
でも Ruby も普及拡大につれて、それなりの規模でのチーム開発が普通になっていった。そんな中
たのは、静的な型がないために引数や返り値の型検証をテストを書くことで保証する必要性があった
というのも理由のひとつだったんじゃないかな。
いっぽう、新しく生まれた静的型付け言語たちは型推論を備えるようになった。型推論とはいちい
ち型を書かなくても、処理系が文脈から型を予測して型付けしてくれる機能のこと。もちろんすべて
のケースで省略できるわけじゃないけど、形を書く煩雑さからプログラマをある程度開放してくれた。
50
4-1. TypeScriptは今やメジャー言語
「んん――」
「まあ口だけの説明じゃ、わかりづらいよね。ちょっと私の PC の画面を見てくれる?」
る!」
の型を書かなくていいというアドバンテージは薄まり、いっぽうで堅牢なコードが効率よく書ける静
的型付け言語のメリットが強調されるようになった。これが近年のプログラミング言語のトレンドだ
ね」
「そうだったんですね。知りませんでした……」
「はあああ、目から鱗が落ちた思いです。なんか俄然、TypeScript やる気になってきました!」
*5統合開発環境。コンパイラ、テキストエディタ、デバッガなどをひとつの UI から利用できるように
したもの。VSCode のような高機能エディタもそれに含まれる。
51
第4章 型のあるTypeScriptは強い味方
4-2. 型のバリエーション
行して。インストールしたらパスを通すために、シェルを再実行してね」
「……っと。はい、終わりました」
の 6 種類になります」
・ number (ex. 3)
・ symbol
・ null
・ undefined
す」
「なるほど。それぞれの型の値の真偽を教えてもらえますか?」
にインストールしとくといいよ。
ですぐ確認できて便利だね」
52
4-2. 型のバリエーション
> n = null;
null
「strictNullChecks オプションが有効になってないからね。ちょっと第1章で生成したプロジェクト
のルートディレクトリから、tsconfig.json というファイルを今いるカレントディレクトリにコピって
> s = null;
[eval].ts(2,1): error TS2322: Type 'null' is not assignable to type 'string'.
「なるほどー」
「あとついでに型推論についても、さらっとデモしてるので見ておいて。typeof 演算子を使うと、文
も string 型になってるのがわかるでしょ?」
「あ、ほんとだ」
53
第4章 型のあるTypeScriptは強い味方
「こんな感じで、わざわざ型を指定しなくてもその型が明らかな場合は、処理系が推測して適切な型
をつけてくれる。これを『型推論』と呼ぶの。VSCode なら、任意の変数の上にマウスカーソルをホ
バーさせると、ポップアップでその型を教えてくれるよ」
「へー、便利ですね」
値に Null を許容したい場合はどうするか。この後で改めて説明する共用体型というものを使います」
いけるんですか?」
「できるよ、ほら」
「さらには、こんなこともできる」
54
4-2. 型のバリエーション
列リテラル型と呼ばれるものなの。単独ではあまり使い途がないけど、
『|』 で列挙することで enum
は、その名の通りどんな型の値でも受けつけるようになります」
場合とか、事前に一律の型を当てはめるのが難しかったりする。そういうときはやむをえず any を指
定するよね」
「ふーん、なるほど」
「次に never。これは何ものも代入できない型、ということなんだけど」
「え? 何も代入できないなら使いようがなくないですか?」
*7 https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/04-typescript/04-never.ts
55
第4章 型のあるTypeScriptは強い味方
case 'cheetah':
return 'Hello, Cheetah!';
default:
const check: never = friend;
}
};
「default 節のとこ、これ何やってるんですか?」
エラーを出しました」
ない。だからコンパイルできないって怒られてるの。こうやって使うことで、case 文の漏れを未然に
チェックすることができるわけ。裏技ぽいけど、実際の現場では重宝するよ」
「なるほどー。うーん TypeScript、奥が深いですね……」
4-3. 配列とオブジェクト
「それじゃ次は配列についてね」
「上と下、どっちでも同じことなんだけど、一般的なコーディングルールでは後者のジェネリクスを使
ったものではなく、前者のようなスタイルを要求されることが多いので、前者の形で書きましょう」
56
4-3. 配列とオブジェクト
「はい」
「続いては、オブジェクトの型について」
interface User {
name: string;
age?: number;
}
const jane: User = { name: 'Jane', age: 27 };
const Jack: User = { name: 'Jack' };
「ざっと書いてみたけど、わかるかな?」
「まず、オブジェクトの型は最初の例のように、変数宣言時に直に書くことができるけど、interface
文で定義することもできると?」
「そう。それから?」
「インターフェースの定義では、プロパティ名の後ろに『?』がつくと省略可能になる?」
「そうだね」
「type はインターフェース型を代入するためのものでしょうか」
なんだけど、単独で使うより型同士の合成時に使われることが多いね。こんな感じに」
57
第4章 型のあるTypeScriptは強い味方
「んん、ちょっと入り組んでますね。えっと……」
「型の合成の説明については、以下にまとめてみたので、今のコードと照らし合わせながら読んでみ
て」
た型のすべてのプロパティを備えるが、同じ名前のプロパティが省略可能と必須だと、必須
が優先される。
「React ではコンポーネントの引数の型合成を行うことが多いんだけど、そちらでは『&』を用いる交
で複数回定義すると上書きじゃなく継承されていくの。こんな感じ」
「……これは気持ち悪いですね」
じゃ最後に、const で配列やオブジェクトの変数を宣言したときの注意してほしい点について説明
しておこうか」
58
4-3. 配列とオブジェクト
「えっ、const で宣言した変数って不変なんじゃなかったでしたっけ」
「うん、だから変数自体の再代入とかはできないよ。でも各要素の上書きや追加はできちゃうんだな、
これが」
「なんか中途半端ですね」
」
「だから安全性のためにどうしても配列やオブジェクトを中身までイミュータブルにしたい場合、こ
うコードが冗長になってめんどくさいので、あまりメジャーなやり方にはならなかったみたい。で、
とができるようになったの」
「へえ、よさそうじゃないですか」
「うん、私もそのうち導入したいと思ってる。でもリリースされたばかりで使用実績があまりなくて、
*8 https://immutable-js.github.io/immutable-js/
*9 https://github.com/mweststrate/immer
59
第4章 型のあるTypeScriptは強い味方
だから今のところは書く側が気をつけて、インデックスを指定して値を上書きするようなコードを
書かないようにしましょう。たとえばオブジェクトの任意の部分を変更したオブジェクトが必要な場
合、そのプロパティを直に書き換えるんじゃなくて、こんなふうに書くといいよ」
「なるほど、スプレッド演算子を使って任意の要素だけを置き換えた新しいオブジェクトを返すんで
すね。このやり方なら副作用を生まずに処理できますね」
4-4. 関数の型定義
「次は関数の型定義ね。TypeScript では関数を宣言するとき、戻り値は型推論で省略できるけど、引
数は必ず指定する必要があるので気をつけて。下のサンプルでは、あえて戻り値の型は省略せずに書
いてます」
60
4-5. コンパイル設定
「アロー関数では戻り値の型宣言って、引数カッコの後に書くんですね」
「最初の変数名の横に書きそうになるよね。でも、それだと無名関数を定義したときにその中で戻り
値型を定義できない。こういうものだと思って受け入れましょう。
それでさっき関数の戻り値の型は明示しなくても型推論が効くと言ったけど、その推論した戻り値
の型を抽出する方法があるの。ReturnType というもの」
「へー。でもこれ、どういうときに役立つんですか?」
「複数の関数の戻り値型をまとめて共用体型を作りたいときとか。今はイメージしにくいかもしれな
くるよ」
「なるほど、先回りして教えてくれてるわけですか。ありがとうございます!」
4-5. コンパイル設定
tsconfig.json の中身ね」
{
"compilerOptions": {
61
第4章 型のあるTypeScriptは強い味方
"target": "es5",
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve"
},
"include": [
"src"
]
}
「TypeScript のコンパイラは明示的に指定されなかった場合、実行されたカレントディレクトリから、
「はい」
「それぞれのオプションが何を意味するかは、随時公式サイト で随時参照してね。ひと昔前は、開
*1
tsconfig はほぼ完璧なので、何も考えずそのまま使えます。いい時代になったよね……(遠い目)
」
「ふーん、そうなんですね……」
パスインポートがサポートされていないの」
「絶対パスインポート?」
62
4-6. モジュールの型定義
「これを見てみて。上の行が相対パスインポート、下の行が絶対パスインポート」
「なるほど。これだけではアレですけど、たとえばディレクトリをまたがったときに相対パスインポ
「そう。わかりやすいしファイルを移動させたときにいちいちパスを書き直さなくてもいいから、ぜ
向をチェックしながらリリースされ次第、既存のコードも絶対パスに書き換えていく予定なのでおぼ
えておいて」
「わかりました」
4-6. モジュールの型定義
「それはそうですね」
「おおー」
63
第4章 型のあるTypeScriptは強い味方
わけだから。でもまだまだ、そういうモジュールは多数派とは言える状況じゃない。そこで
DefinitlyTyped*12 の出番になる」
「なんかかっこいい名前が出てきましたね」
「DefinitlyTyped は、有志のユーザーたちが自分の使いたいモジュールの型定義ファイルを作り、集め
の Web インターフェースのページでは表示しきれないくらい。
@types/react-router を実行してみる」
すといいよ。自前で用意されてる場合は『非推奨:このパッケージはフェイクで、オリジナルのライ
ブラリが型定義ファイルを提供しているので、これをインストールする必要はありません』と表示さ
れるから」
「へー、親切ですね」
が完全には保証されてないことをおぼえておいて。またオリジナルのバージョン更新についていけて
なくて、最新版に対応していないこともある。私も実際に入れてみて、部分的に動かなかったことが
あるし」
「ありゃー」
ーズとか出ちゃうよね」
「あはは、柴咲さんかわいいー」
*12 http://definitelytyped.org/
64
5-1. JSXとは何であるか、何ではないのか
第 5 章 拡張記法 JSX
で見た限りだとちがくて、なんかロジック部分とビュー部分が混在しているようでとっつきづらく感
じました」
ックを埋め込んだほうがわかりやすいと思うんですが」
複数のサーバとの非同期な通信にそのデータのキャッシュ、ローカルに永続化されたデータとされて
ないデータ。それと同時に、選択中のタブやページネーションといった UI の状態も管理する必要が
刻一刻とタイムラインが書き換わり、リアルタイムに返事やいいね!の数が更新されたり、それらの
通知アラートが来たり、同じ画面の中で複数ユーザーとの同時チャットもできたりするよね」
「複雑な問題を解決するために人間が採る普遍的なアプローチは、大きく複雑な対象を小さく単純な
65
第5章 拡張記法JSX
分割より細かく分割するのは難しいよね」
「そうですね」
を独立性の高いコンポーネントという単位に分割し、そこにロジックとデザインを完結させて閉じ込
が完結した無数のコンポーネントを組み合わせていくほうが、より複雑なアプリケーションを破綻な
ンダリングされていくの」
ちがいますね」
「そう。そして細かく分割したコンポーネントは、それ自身が受け取ったデータによって大きく振る
JavaScript でコンポーネントを記述することを選んだ。これはまあ好みかもしれないけど、ロジック
のロジック部分を記述する制御構文そのテンプレート言語の独自リテラルで、表現力が限られていて
凝ったことをするのが難しかったし」
「うーん、なんとなくわかります」
ションの抽象化に適しているということの証左とも言えるよね。これらはそれぞれ、用意された XML
ロントエンドを対象のプラットフォームにした際のコンポーネントを記述するためのひとつの方便で
で思想がちがうんだよ」
「なるほど。それにしても柴咲さん、アツく語りますね……」
66
5-2. JSXの文法
からね。この話はまあ、このへんにしておくけど。
ドを見てもらおうかな」
<h1>Hello, world!</h1>
React.createElement('h1', 'Hello, world');
「JSX ではこのふたつは全く同じ意味になるの」
シンタックスシュガーってことですね」
「その通り。後者だけでコンポーネントを最後まで書ききることはできるけど、実際のプログラミン
グでは複数の階層が入りくんでくるわけで、そうなるとまあ読みづらいよね。JSX をキモいと批判す
る人たちの中には、後者のほうがわかりやすいと言い張る人もいるけど、そのやり方を貫徹している
「それはそうでしょうね……」
「最初の
『Hello, Wold!』
で生成されたファイルも拡張子は .tsx でしたね。
JavaScript ベースと TypeScript
ベースということ以外に、ふたつつのあいだに他に違いはあるんですか?」
「じゃあ、JSX の文法をざっと説明していこうか」
67
第5章 拡張記法JSX
「そう単純でもなくてね。たとえば次のタグ属性はこう置き換えられてる」
・ class → className
・ for → htmlFor
「あ、JavaScript の予約語とかぶってるんですね?」
「変数名とかと同じ扱いなわけですね」
「そうだね。あとはテンプレート言語でいう制御構文のような書き方をどんなふうにやるか、最初に
てみよう」
return (
<div className="App">
<header className="App-header">
{
// コ メ ン ト は こ う 書 く
}
<img {...logoOptions} />
{title && <p>{title}</p>}
{targets.map(target => (
<p>Hello, {target}!</p>
))}
68
5-2. JSXの文法
</header>
</div>
);
}
}
「まず変数の埋め込みから」
むと値が展開されるわけですか」
「なるほど」
「じゃ次は繰り返し処理について」
いいのか」
「そう。応用として、一定の条件にマッチしたものだけを表示したい場合は、たとえば targets.filter(t
「おお―――っ」
『...』で展開されてますね」
「こうすることで、オブジェクトに格納されたタグ属性値をまとめて渡すことができるの」
「へえー、なんかオシャレな書き方ですね」
「こういうのがさらっと使いこなせるようになると、中上級者と言えるかな」
「早くそのレベルになれるよう、がんばります!」
69
第5章 拡張記法JSX
使うようにしましょう」
「なんで上の例では閉じカッコを改行してるんですか? 一行にしちゃいましょう……って、あれ?
エラーになっちゃった」
補足として忘れちゃいけないポイントを挙げておくと、JSX でタグを階層化して書くときは、ツリ
ー階層のトップレベルはひとつにしないといけないから気をつけて。上のコードでは階層の一番上は
はできないの」
「私もよく忘れてこれやっちゃうので、気をつけましょう」
「はい!」
70
6-1. ESLint
6-1. ESLint
の Linter じゃないんですか?」
「はー、それにしても移り変わりの激しい世界ですねえ……」
「まあ今の状況じゃ、しょうがないよね。でも一から学ぶにはシンプルでわかりやすい方向に行って
るんだから、拒否するべき変化じゃないよ」
「確かに」
World』プロジェクトをまるごとコピってきて作業するよ。そのルートディレクトリでこのコマンド
を実行しましょう」
71
第6章 LintとPrettierでコードをクリーンに
をインストールするのはやめたほうがいい」
「なるほど」
こともできるんだけど、現状そのままでは使いものにならないので、あらかじめ私が調整しておいた
くんですね」
「 う ん 、 そ の 通 り 。 主 要 な ル ー ル は 最 初 の ほ う で 読 み 込 んで る eslint:recommended と
plugin:@typescript-eslint/recommended に定義されてるので、ここでは最低限必要そうなものだけを
追加・上書きしてる。各ルールの意味は公式サイト 等で確認して。
*3
「じゃ、次の設定を VSCode メニューの Code > Preferences > Settings で開いたタブの右上『{ }』ア
おいて」
"eslint.autoFixOnSave": true,
"eslint.enable": true,
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
*2 https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/06-lint/01-eslint/.eslintrc.js
*3 https://eslint.org/docs/rules/
72
6-1. ESLint
に settings を置けば、開発チーム内で同じ設定内容を共有できるよ、というのは前にも言ったね。こ
のサンプルコードでもそうなってるので確認しておいて」
「わかりました」
「あっ、書き換えた部分に赤の破線が表示されて、そこにマウスカーソルをホバーさせると『Unexpected
と、ファイル保存時にルールに沿った簡単な整形をしてくれるようになるよ」
73
第6章 LintとPrettierでコードをクリーンに
6-2. Prettier
は、インデントや改行箇所といったところにまで介入して、より積極的に書き換えてくれるの」
「えっ、なんか強引……」
「ふふっ、すぐに慣れて気持ちよくなってくるからたいじょうぶ」
「ええ――――っ?」
「私も昔は、より見やすい改行とかにこだわったりしたけど、Prettier を使うようになってからは『コ
ードの整形なんか、わざわざ人間がやるような仕事じゃない』と考えるようになったよ。どんなにこ
服させられるのは逆に気が楽なんだよね」
「……はあ、そういうものですか」
る必要がある」
「大変そうですね」
「でもだいじょうぶ。後で私の設定を丸ごとコピらせてあげるから」
「先輩! ありがとうございます!!」
74
6-3. 組み合わせとカスタマイズ
6-3. 組み合わせとカスタマイズ
ビリティを考慮したルールとか」
「へえ――」
モジュールもインストールしてある。今回、追加したモジュールのリストはざっとこんな感じね」
・ eslint
・ eslint-config-airbnb
・ eslint-config-prettier
・ eslint-plugin-import
・ eslint-plugin-jest
・ eslint-plugin-jsx-a11y
・ eslint-plugin-prettier
・ eslint-plugin-react
・ eslint-plugin-react-hooks
・ @typescript-eslint/eslint-plugin
・ @typescript-eslint/parser
・ stylelint
・ stylelint-config-prettier
・ stylelint-config-standard
・ stylelint-order
・ prettier
*4 https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/06-lint/03-mysetting
75
第6章 LintとPrettierでコードをクリーンに
・ prettier-stylelint
・ husky
・ lint-staged
「ひいっ、何ですかこれ。複雑すぎじゃないですか? こんなの自分で設定できる気がしません……」
「うーん、私でもこの設定の作り込みに公式サイトや国内外のブログ記事を読み込んで、試行錯誤し
ながら半日はかかったからね。過渡期だからしょうがない」
「……そんなこと言われても」
インデントに余分なスペースを入れてみたり、行末のセミコロンを省略してみたり、改行場所をぐち
ゃぐちゃにしてみたり、ダブルクォートをシングルクォートに変更してみたりしてから、を command
+ S を押してみてくれる?」
「あっ、すごい! 保存と同時にぐちゃぐちゃにしたコードが一瞬で手直しされた!」
「これなら、この先気持ちよくコーディングできそうです!」
76
7-1. Reactの基本思想
第 7 章 何はなくともコンポーネント
「React にはその設計を支えている重要な概念がいくつかあります。公式が自己定義しているわけじ
ゃないけど、よく取り上げられるのが以下の3つね。聞いたことはある?」
・ 仮想 DOM(Virtual DOM)
・ コンポーネント指向
・ 単方向データフロー
「
『仮想 DOM』はときどき耳にしますね。意味はあんまりわかってないですけど。
『コンポーネント
指向』はコンポーネントをベースに開発するってことでしょうか? 『単方向データフロー』は……。
すいません、さっぱり見当がつきません」
新はブラウザにとって各種オーバーヘッドが大きくて、それを最適化しようとすると非常に高いコス
トエンド開発者が何も考えなくてもそれらのオーバーヘッドを最小限に抑えてくれるようになった。
これが仮想 DOM」
「なるほど」
ていくことでアプリケーションを作ることを目指してる」
「
『目指してる』というのは、つまりまだ実現されてない技術ってことですか?」
77
第7章 何はなくともコンポーネント
ワーな人たちはすでに実戦投入可能な技術だと主張するかもしれないけど、私はなかなかその評価は
ムワークを組み合わせる必要があるの。でもそうすると、冗長な抽象階層が増えるメリットがあまり
感じられなくなるといった問題もあって」
は邪道だと言うかもしれないけどね。
ら Facebook の自社プロダクトに使われていたんだけど、それがオープンソースソフトウェアとして
「はー、そんな歴史があったんですか」
「うん。で、最後の単方向データフローなんだけど、これはあまり他のフレームワークに真似されな
かった。だから今となっては逆に、React を特徴づける概念と言えるね。データの受け渡しについて
は簡単に言うと、テンプレートに埋め込まれた任意の変数が別のところに紐付けられていて、その変
更がリアルタイムに反映されるというもの」
「なるほど、わかりやすいです」
「
『わかりやすい』か……。たぶん初学者にとってはそうなんだろうね。でもこのやり方はコンポーネ
ントの独立性という側面から見るとあまりよろしくない。たとえばアプリが複雑になって、色んな方
向にバインドしてる変数がそこらじゅうに散りばめられ、コンポーネントの階層がどんどん深くなっ
ていくと、人間の頭では何が起こるか予測がつきづらくなってくる。思わぬところで思わぬ値が書き
換わって思わぬ事態を引き起こしかねない」
「うーん、設計しだいな気もしなくもないですけど……」
78
7-2. Propsをコンポーネントに受け渡す
「でもコンポーネント志向というのは独立性の高いコンポーネントを組み合わせてアプリを構成する
考え方なわけで、コンポーネントの中身が知らないところで勝手に書き換わるのは、React の開発者
トへ一方通行で渡されます。下の階層から上の階層にデータの変更を反映させることはできないの」
「すごくストイックな考え方のように思えますけど、それで不都合はないんですか? たとえばユー
ザーのフォーム操作によって、上の階層のコンポーネントの中身を書き換える必要があるときとか」
「うん、なかなか鋭い質問だね。それにはいくつか方法はあるんだけど、たとえばそのひとつは、親
コンポーネントが自身の状態を変更する関数を子コンポーネントに渡して、フォーム入力時に発火さ
れるイベントにその関数を仕込んでおくというやり方だったりする」
「えっ、えっ?」
「んー、まあ今の段階ではまだ理解はむずかしいかな。React は特にそうなんだけど、まず基本の思
想を把握して、多方面の基礎知識を積み上げたうえで実際のコードに立ち会わないと、なかなか応用
問題が解けるようにならない」
「……うう、早くわかるようになりたいです」
「まあまあ、そう焦らずに。今は単方向データフローとは、親コンポーネントから子コンポーネント
に一方向でしかデータを受け渡せないやり方ということを知ってればいいから。
数として定義される」
「はい」
ね。これについて、くわしく説明していこう」
「Props とは関数に対する引数のようなもの、と考えてもらえればいいかな。マウント時のタグの中
79
第7章 何はなくともコンポーネント
では、そのタグの属性値として表現され、コンポーネント自身の定義の中ではそれがクラスコンポー
て表現される」
「クラスコンポーネント? 関数コンポーネント?」
「あー、ごめんごめん。まだその話をしてなかったね。それについてはまたあらためて説明するけど、
てみて」
「……これは?」
「高校バドミントンのスポ根漫画『はねバド!』のキャラ一覧だね」
「柴咲さんって、意外にこういうのが好きなんですね……。でもかくいう私も漫画、大好きですよ。
気が合いますね!」
「はいはい、そういった話は休憩時間にね」
「はーい。それじゃあえっと画面なんですけど、なんか見た目がちょっとリッチになってるみたいで
*1 https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/07-component/02-props
80
7-2. Propsをコンポーネントに受け渡す
すよね?」
てくれてるんですね」
ゃ、中身のコードを見ていこう。src/App.tsx はどうなってるかな?」
import './App.css';
return (
<div className="container">
<header>
<h1> は ね バ ド ! キ ャ ラ ク タ ー 一 覧 </h1>
</header>
<CharacterList school=" 北 小 町 高 校 " characters={characters} />
</div>
81
第7章 何はなくともコンポーネント
);
}
}
「他のファイルでエクスポートされたオブジェクトや関数、型インターフェースもろもろを読み込んで
前を指定しないとインポートできないよ」
「はい」
つけることもできるよ。モジュール同士で名前がバッティングしちゃったときとかに重宝する」
て」
オブジェクト配列が設定されてますね」
CharacterList.tsx のほうのコードを見てみようか」
82
7-2. Propsをコンポーネントに受け渡す
height?: number;
}
interface CharacterListProps {
school: string;
characters: Character[];
}
return (
<>
<Header as="h2">{school}</Header>
<Item.Group>
{characters.map(c => (
<Item>
<Icon name="user circle" size="huge" />
<Item.Content>
<Item.Header>{c.name}</Item.Header>
<Item.Meta>{c.age} 歳 </Item.Meta>
<Item.Meta>
{c.height ? c.height : '???'}
cm
</Item.Meta>
</Item.Content>
</Item>
))}
</Item.Group>
</>
);
}
}
インターフェースはコンポーネントを定義するクラス宣言のところで呼ばれてる」
かったジェネリクスみたいなのがあります。これは何ですか?」
83
第7章 何はなくともコンポーネント
{} という空オブジェクトが設定されているので、Props が必要なかったこれまでは省略してただけ」
「あ、そうだったんですね」
「こうすることで、そのコンポーネントをタグとしてマウントするときに必要な属性値とその型が決
に適当な属性値を追加したりすると、赤の波線アンダーラインが出て『型が違う』って指摘されます
ね」
「そうやって自分でいろいろ試してみて、実際にエラーを出して確認するのはいいことだね。いっぽ
characters の要素をローカル変数として抽出してるわけだけど」
「へー、こういう書き方ができるんですね」
あります」
こう記述しておくとそれが避けられるの。ただ、<div> とちがって必ず中身のノードが必要なので、
処理の結果、中身がなくなる可能性のあるときは使っちゃダメだよ」
「わかりました」
「これは、
『※その要素は省略できます』ってことだね。だから泉理子さんの height 値は設定されて
84
7-2. Propsをコンポーネントに受け渡す
「なるほどー。こうして説明されると、最初とっつきづらそうでわけわかんなかったコードが、すご
くキレイに見えてきました」
る。もちろん、省略された場合の処理はちゃんと考えておく必要があるけどね」
「へー、了解です!」
「よし。ところでこのコード、一見正しく動いてるようだけど、実は不具合があるの」
「えっ、そうなんですか?」
って怒られてます」
{characters.map(c => (
- <Item>
+ <Item key={c.id}>
ね。
なくても動きはするんだけど、
パフォーマンスのために大事だから、
ないと指定するように Warning
が表示される。私もよく忘れがちなので気をつけて」
「なるほど、おぼえておきます!」
85
第7章 何はなくともコンポーネント
「コンポーネントで最も大事なのは以下の 3 つの要素ね。ここ、テストに出るからねー」
・ Props
・ Local State
・ ライフサイクル
なってまけど、じゃ『Global State』もあるってことですか?」
「うん、あいかわらずいい質問をしてくれるねー。厳密には『Global』とはいわないんだけど、どの
それ自体がその機能を持ってるわけじゃなくて、別のライブラリをインストールする必要があるんだ
けど。
身が内部に持つ状態のこと。じゃ、サンプルコード を実行してみようか」
*2
*2 https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/07-component/03-state
86
7-3. コンポーネント内部の状態を規定するLocal State
「カウンターですね。
『+1』を押したら 1 が加算されて、
『-1』を押したら 1 を減算されていきます」
のコードを見ていこう」
import './App.css';
interface AppState {
count: number;
}
increment() {
this.setState(prevState => ({
count: prevState.count + 1,
}));
}
decrement() {
this.setState(prevState => ({
count: prevState.count - 1,
}));
}
render() {
const { count } = this.state;
return (
<div className="container">
<header>
<h1> カ ウ ン タ ー </h1>
</header>
<Card>
<Statistic className="number-board">
<Statistic.Label>count</Statistic.Label>
<Statistic.Value>{count}</Statistic.Value>
</Statistic>
87
第7章 何はなくともコンポーネント
<Card.Content>
<div className="ui two buttons">
<Button color="red" onClick={() => this.decrement()}>
-1
</Button>
<Button color="green" onClick={() => this.increment()}>
+1
</Button>
</div>
</Card.Content>
</Card>
</div>
);
}
}
「まず注目してほしいのが、AppState という型インターフェース」
ているんですね。あと、今回はクラスの中にコンストラクタがありますね」
「カウンター値を0に初期化してるんですね」
ドを使うの」
「ほんとだ。this.state.count に値を導入してみたら、
『[ts] Cannot assign to 'count' because it is
「参照だけなら普通にできるんだけどね。でね、setState() の引数には、以下の2種類が設定できる
の」
88
7-3. コンポーネント内部の状態を規定するLocal State
を返す関数。
「ここでは2を使ってるわけだけど、Props は使わないので省略してる。どちらを使うかは、State に
固定値を設定するときは1、動的に変更したい場合は2を選びましょう」
「わかりました」
てください」
したものだね。Button コンポーネントに対して、属性値としてその関数が渡されてる。前に単方向デ
ータフローの説明をしたとき、秋谷さんから『ユーザーのフォーム操作によって、親コンポーネント
の中身を書き換える必要があるときはどうするんですか?』って質問されたよね」
「……そういえば」
「それに対して私は『親コンポーネントが自身の状態を変更する関数を子コンポーネントに渡して、
フォーム入力時に発火されるイベントにその関数を仕込んでおく』って答えたと思うんだけど、これ
イベントに仕込まれてるの」
「あっ、ああ――――! 今、納得いきました。そうか、そういうことなんですね」
「うん、わかってくれて私もうれしいよ。じゃ、もうひとつ過去のすっきりしない疑問を解消しとこ
渡してやれば簡単だと思わない?」
「そう言えばそうですね。どうしてそうしないんでしょうか?」
「じゃ、そう書き換えてみようか」
89
第7章 何はなくともコンポーネント
「これがアロー関数と従来の関数定義の話をしたときに説明した、this の挙動のちがいなんだよね。
なる」
「おおお――。なるほど―――」
「だからメンバーメソッドの定義のほうをアロー関数に書き換えてみよう」
- decrement() {
- this.setState(prevState => ({
- count: prevState.count - 1,
- }));
- }
+ decrement = () =>
+ this.setState(prevState => ({
+ count: prevState.count - 1,
+ }));
+ }
- increment() {
- this.setState(prevState => ({
- count: prevState.count + 1,
- }));
- }
+ increment = () =>
+ this.setState(prevState => ({
+ count: prevState.count + 1,
+ }));
+ }
90
7-3. コンポーネント内部の状態を規定するLocal State
「あ、これで『+1』ボタンがさっきまでと同じように正常に動くようになりました!」
「うん。this の挙動のちがい、これで理解できたかな?」
「あ――、なんかずっと残ってたモヤモヤが晴れました! 本当はあのときは『うーん、なんだか煙
に巻かれたなあ』と思ってたんですけど、ごめんなさい!」
「あはは、こっちは素直な反応が見れて教えがいがあるけどね。でもあと最後にもう一点。increment
と decrement は実はこんなふうにも書けることをおぼえておいて」
「メソッドに e という引数が設定されてますね。これって何ですか?」
ン ト ハ ン ド ラ を 使 っ て メ ソッ ド 内 部 で 何 か 操 作 し た い 場 合 、 引 数 と して 受 け 取 れ る 。
とクリックでページ移動が起きてしまうので、それをキャンセルするためにこういう記述が必要にな
るんだよ」
「へー、なるほど」
りのライフサイクルの話をしようか」
91
第7章 何はなくともコンポーネント
7-4. コンポーネントのライフサイクル
「
『ライフサイクル』とは、たとえばマーケティング分野では『ある製品が開発され、市場に投入され、
発展普及し、やがて廃れて姿を消すまで』の過程を示す用語のことね。コンポーネントのライフサイ
クルとは、初期化されてマウントされレンダリングされ、何らかの処理が行われて再レンダリングさ
れたりして、最後にアンマウントされるまでの過程。途中、何度も再レンダリングされることがある
ので、
『サイクル』という言葉がピッタリだと思う。
このライフサイクルには、大きく分けて以下の4つのフェーズがあります」
2. Updating …… 変更を検知してコンポーネントが再レンダリングされるフェーズ
「すいません、2の『変更を検知して再レンダリング』のところなんですが、何の変更についてなん
でしょうか?」
「うん、いい質問だね。コンポーネントが再レンダリングされるのは、基本的に2つの場合のみなの。
のタイミング。ただしそこに介入して、任意の条件で再レンダリングを阻止することはできるけどね」
が変更されたから、コンポーネントが再レンダリングされたんですね」
「その通り。それで、このライフサイクルの各フェーズに介入して任意の処理を差し込むことができる
メソッドが、React のコンポーネントには用意されてるの。これをライフサイクルメソッドといいます。
それらをフェーズごとに表にまとめてみたのがこれね」
92
7-4. コンポーネントのライフサイクル
1. Mounting フェーズ
メソッド 戻り値 説明
constructor(props) void コンストラクタ
static getDerivedStateFromProps(props, State | null レンダリングの直前に呼ばれ、戻り値で
state) Local State を変更することができる
render() React.ReactNode レンダリングを行う
componentDidMount() void コンポーネントがマウントされた直後に
呼ばれる
2. Updating フェーズ
メソッド 戻り値 説明
static getDerivedStateFromProps(props, State | null レンダリングの直前に呼ばれ、戻り値で
state) Local State を変更することができる
shouldComponentUpdate(nextProps, boolean 再レンダリングの直前に呼ばれ、false
nextState) を返せば再レンダリングを中止できる
render() React.ReactNode レンダリングを行う
getSnapshotBeforeUpdate(prevProps, Snapshot | null コンポーネントが変更される直前に呼ば
prevStat) れ、戻り値でスナップショットを取って
おける
c o m p o n e n t D i d U p d a t e ( p r e v P r o p s , void コンポーネントが変更された直後に呼ば
prevState, snapshot?) れる
3. Unmounting フェーズ
メソッド 戻り値 説明
componentWillUnmount() void コンポーネントがアンマウントされる直
前に呼ばれる
93
第7章 何はなくともコンポーネント
メソッド 戻り値 説明
componentDidCatch(error, info) void 子孫コンポーネントで例外が起きたとき
に呼ばれる
static getDerivedStateFromError(error) State 子孫コンポーネントで例外が起きたとき
に呼ばれ、State を更新する
「おおー、なんかたくさんありますねえ」
「まあ全部暗記する必要はないけど、なんとなくあんなことができるメソッドがあったなレベルでお
「コンポーネントのライフサイクルに対して、各フェーズとそれぞれのメソッドを当てはめた図がこれ
ね」
「おおー。この図、わかりやすいですねー」
94
7-4. コンポーネントのライフサイクル
「他に注意点として、ネットの古い記事に注意してほしいかな。React コンポーネントのライフサイク
というライフサイクルメソッドがあったの。これらはレンダリングの直前に実行されるメソッドで、け
っこう多用されてたんだけどバージョン 17 から有効化される、最適化された完全非同期なレンダリ
ングと相性が悪くて副作用を生みやすいので、16.3 より後からは公式から非推奨になってるのね。そ
「へー。そうか、だから古い記事を見ると、それら非推奨のメソッドがバリバリ使われてたりするこ
とがあるわけで、それを参考にしちゃうと痛い目をみることがあると……」
「そういうこと。気をつけてね。
コマンドでサンプルコード*3 を実行してみて」
「60 秒でリセットされるカウントダウンタイマーですね。途中、ボタンでもリセットできるようにな
ってます」
*3 https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/07-component/04-lifecycle
95
第7章 何はなくともコンポーネント
import './App.css';
interface AppState {
timeLeft: number;
}
reset = () => {
this.setState({ timeLeft: LIMIT });
};
tick = () => {
this.setState(prevState => ({ timeLeft: prevState.timeLeft - 1 }));
};
componentDidMount = () => {
this.timerId = setInterval(this.tick, 1000);
};
componentDidUpdate = () => {
const { timeLeft } = this.state;
if (timeLeft === 0) {
this.reset();
}
};
componentWillUnmount = () => {
clearInterval(this.timerId as NodeJS.Timer);
};
timerId?: NodeJS.Timer;
render() {
const { timeLeft } = this.state;
return (
<div className="container">
96
7-4. コンポーネントのライフサイクル
<header>
<h1> タ イ マ ー </h1>
</header>
<Card>
<Statistic className="number-board">
<Statistic.Label>time</Statistic.Label>
<Statistic.Value>{timeLeft}</Statistic.Value>
</Statistic>
<Card.Content>
<Button color="red" fluid onClick={this.reset}>
<Icon name="redo" />
Reset
</Button>
</Card.Content>
</Card>
</div>
);
}
}
「うわ長い!」
「これまでと比べればね。実際の業務コードではこんなの序の口だよ。ざっと見たところ、どう?
読める?」
算されますね。で、その次からがライフサイクルメソッドですね。コンポーネントが最初にマウント
に延々と実行し続けるようにするものだね」
さ れ る よ う に な る わ け で す ね 。 そ れ か ら 、 コ ン ポーネ ン ト が 変 更 さ れ た 直 後 に 呼 ば れ る
componentDidUpdate() の中で、
Local State の timeLeft の値が 0 になったら reset() メソッドを呼ぶと。
コンポーネントがアンマウントされる直前に、えーっと……」
のタスクが生き続けてしまうから、コンポーネントがアンマウントされるときに clearInterval() で
97
第7章 何はなくともコンポーネント
その ID を指定してタスクを止めてあげるの」
ると。はい、だいたいわかりました!」
「うん、よくできました」
7-5. 関数コンポーネント
「コンポーネントの説明に関してはこれが最後。関数コンポーネントについて学んでいきましょう」
て、柴咲さん説明されかけてましたよね。でもこれまで見たコンポーネントは全部クラスで定義され
てましたけど」
「うん。コンポーネントはクラスだけじゃなく、関数としても定義できるの。というより、正しい React
Way に則せばコンポーネントは関数で定義できるときは関数で定義するべきだね。ここにいたるまで
式的な見解として、その5年以上の開発の歴史を経てクラスコンポーネントではなく、関数コンポー
ネントで書くことを優先するよう開発者にアナウンスしているの。理由としては、コードがシンプル
に書けるというのはもちろんだけど、他にも以下のようなことが挙げられてる」
・ 記述が冗長になりがちで、時系列が複雑なライフサイクルメソッドの挙動
・ 今後導入予定の各種最適化がクラスだと難しい
「ええー? クラスコンポーネント、がんばって勉強してきたのに使われなくなっちゃうんですか?」
「すぐになくなるわけじゃないし、今のところクラスコンポーネントを廃止する計画はないって言っ
てるけど、新しく作るコンポーネントは関数で書くように推奨されてるね」
「……そうなんですね」
98
7-5. 関数コンポーネント
「でも関数コンポーネントで、クラスコンポーネントを全て置き換えることはできるんですか?」
ことができない。そしてライフサイクルメソッドを備えられない。これらはクラスコンポーネントでは
そのクラスのメンバーとして実装されていたものだからね」
「うーん。じゃあ関数コンポーネントって、あんまり使い途がないのでは……」
「
『これまでは』って言ったでしょ。事情が変わったの。React は 2019 年 2 月にリリースされたバー
り込ませる機能が追加されたの。これについてはこの次でじっくり説明する予定。今はシンプルな関
数コンポーネントについて学びましょう」
「はーい」
「じゃあ以前使ったサンプルコードの『はねバド!』のキャラ一覧を、関数コンポーネントを使って
interface CharacterListProps {
school: string;
characters: Character[];
}
*4 https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/07-component/05-functiona
99
第7章 何はなくともコンポーネント
<Item>
<Icon name="user circle" size="huge" />
<Item.Content>
<Item.Header>{c.name}</Item.Header>
<Item.Meta>{c.age} 歳 </Item.Meta>
<Item.Meta>
{c.height ? c.height : '???'}
cm
</Item.Meta>
</Item.Content>
</Item>
))}
</Item.Group>
</>
);
「関数コンポーネント、たしかにスッキリした感じですね。えーっと、書き換わった箇所はと……。
この FC というのは?」
Functional Component』って名前だったんだけど、最近のアップデートで関数コンポーネントでも状
態が持てるようになったので『FC』
、『Function Component』に改名されたの」
「あらら、スーパーファミコン(SFC)からファミコン(FC)にダウングレードしちゃったんですか
……」
「いや、秋谷さん? あなたいくつ?」
「えっ、24 ですけど……」
「わかりました」
「じゃ、続きを見ていこうか」
けど?」
100
7-6. Presentational ComponentとContainer Component
らこうやって指定されたデフォルト値で上書きされるけど、Truthy な値だったら引数値がそのまま表
示される」
「だからスッキリ書けるんですね。わかりました。関数コンポーネントで書くときは私もこういうふ
うに書きます」
「うん、src/App.tsx のほうはとりたてて変わったところがないので説明を省略するけど、自分で見
ておいてね。
」
「コンポーネントにはクラスコンポーネント(Class Component)と関数コンポーネント(Function
Component)の2種類があるってことだっただけど、それとは別の側面からコンポーネントをまた2
の会話の中では、ただ『コンポーネント』と『コンテナ』と略して呼ぶことが多いかな」
「クラスと関数じゃない、別の側面での分類ってどういうことですか?」
101
第7章 何はなくともコンポーネント
「ちょっとこの先学ぶことになる概念が先取りして入っちゃってるけど、それらはおいおい理解でき
ればいいとして、だいたいのニュアンスは通じるかな?」
「そうですね。この定義に基づけば、さっきの『はねバド!』キャラ一覧を表示したサンプルコード
はまずないわけですね?」
Props、Hooks といった機能だね」
「いっぺんに新しい機能が3つも出てきた……」
いう機能があるってことだけ認識してくれれば。
それらを踏まえて、React らしくて一番きれいなコンポーネントの作り方をここで説明しておくね。
じゃない」
「それはどうしてですか?」
「そのほうがコンポーネントの再利用性やテスタビリティが高くなるからだよ。Presentational
102
7-6. Presentational ComponentとContainer Component
際に追加した機能だけをテストする、みたいなことがやりやすいの。これもそのうち実践するから」
「わかりました」
「はい、じゃあ長くなったけどコンポーネントの説明はこれで終了。おつかれさまー」
103
第8章 Hooksで関数コンポーネントを強化する
第 8 章 Hooks で関数コンポーネントを強化
する
「最近まで既存のコンポーネントに機能を追加するには、おおまかに2つのやり方があった。ひとつ
「以前、関数型言語について学んだとき、関数を引数にとったり関数を戻り値として返す高階関数が
出てきたのをおぼえてます」
「そう、HOC は高階関数の親戚みたいなものだね。コンポーネントを引数にとり、戻り値としてコ
ンポーネントを返す関数のこと。HOC がどんなふうに使われるか、ごく簡単なサンプルを作ってみ
たので見てみよう」
render() {
return <WrappedComponent />;
}
};
};
104
8-1. Hooks登場以前の話
マウント時に『Component is rendered.』というテキストをコンソールに出力する機能が付与されてい
るわけですか」
「え、そうなんですか? どうして?」
部で使うことができないので柔軟性に欠けるとか、そんな理由だったと思う。そこで公式が HOC に
書くんだけど……」
「……すいません、意味不明です……」
ーネントを意識する必要があって、肝は真ん中の子コンポーネント。親から渡されるの孫コンポーネ
ントに機能を追加していく感じ」
「うーん、言葉だけではなかなか難しいです……」
「サンプルを用意しようかと思ったけどすごく長くなってしまうので、どうしても理解しておきたい
なら公式ドキュメント*1 を読んでおいて」
「えええ―――、そんな投げやりでいいんですか?」
105
第8章 Hooksで関数コンポーネントを強化する
だ』とまで言う人もいるし」
「死んじゃったんだ……」
難しいとか、そもそも概念の理解のための障壁が高くコードも読みづらいとかあってね。HOC に代
わるものとして公式から提案されたものだけど、そのメリットに共感できる開発者がついに多数派に
ものなんだけど……」
「そう思うよね。で、公式が推しているのになかなか普及しない中、これから説明する Hooks の登
*2 https://twitter.com/acdlite/status/971605795501613056
106
8-2. Hooksとは何か
Hooks に移行していくだろうね」
「うん。じゃ、これからその説明をしていこう」
発者のあいだでよく使われていたの」
「へー、そうだったんですね」
『接続する(hook into)
』から Hooks と名付けられたの。
*3 https://github.com/acdlite/recompose
107
第8章 Hooksで関数コンポーネントを強化する
だったんだけど、そのときはまだα版だったというのにコミュニティの反響が大きくてね。Hooks を
使ったライブラリが雨後の竹の子のように出てきて、さらにメジャーなライブラリも将来的に Hooks
に対応することを次々に表明しだした。その影響を受けていくつかの技術が終わりに追いやられたの」
してるくらいだし」
「みたいだね。あと当然ながら、Recompose も作者自身が鞍替えしたわけだから、早々に開発中止と
に水でショックだったな……」
「ええー、それは地味につらいですね」
外部のコミュニティから人や技術を採り入れて進化を続けている技術だからね。
*4 https://twitter.com/acdlite/status/1032363809430626309
108
8-3. State HookでLocal Stateの管理
低いといった点がほぼクリアされていて、コードが読みやすくシンプルになるところだね。さらに state
やライフサイクルを使うといったコンポーネントに付与したい機能をそこだけ切り出すことも簡単な
ので、再利用しやすくテストしやすいといったメリットもある」
トでもできるようになるんですか?」
に大きな問題はないと思う」
「じゃ、将来的にはクラスコンポーネントは使われなくなる方向なんでしょうか?」
いと言うし、何より過去の膨大な資産のほとんどがクラスコンポーネントで作られているだろうから、
ネントで作ることを推奨しているので、ウチのチームもその方針で行くよ」
「ええー? じゃ、なんかクラスコンポーネントを延々勉強してきたのが無駄だったような……」
「いや、でもね。既存のコードはクラスコンポーネントで書かれてることが多いんだから、それが読
要はないって言ってるし。それにライフサイクルの概念も、クラスコンポーネントのライフサイクルメ
ソッドを先に知っていたほうが理解しやすいんだよ」
「……なるほど」
トでも使えるようにする機能だね。使い方はこんな感じ」
109
第8章 Hooksで関数コンポーネントを強化する
数とセッター変数の名前は好きに設定できるよ」
「確かにわかりやすいですね」
「じゃ、次は実際のコードで見ていこう。前にクラスコンポーネントで実装したカウンターのサンプ
src/App.tsx だけ開いて確認すればいいかな」
import './App.css';
*6 https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/08-hooks/03-state
110
8-3. State HookでLocal Stateの管理
return (
<div className="container">
<header>
<h1> カ ウ ン タ ー </h1>
</header>
<Card>
<Statistic className="number-board">
<Statistic.Label>count</Statistic.Label>
<Statistic.Value>{count}</Statistic.Value>
</Statistic>
<Card.Content>
<div className="ui two buttons">
<Button color="red" onClick={decrement}>
-1
</Button>
<Button color="green" onClick={increment}>
+1
</Button>
</div>
</Card.Content>
</Card>
</div>
);
};
「useState() を使っているところのコードは、さっきのと全く同じですね」
「うん。で、どう? 全体的に読んでみて」
「すごくシンプルにまとまってて、読みやすいですね。最初のクラスで作ったものと比べて、行数も 57
行→ 42 行と 15 行も減ってますし」
「でもなんだか魔法みたいで気持ち悪いような気がしないでもないです。クラスならインスタンスの
中でメンバー変数が状態を記憶しているのはわかりますけど……」
「実際は、React モジュールのグローバル空間にそれぞれのコンポーネントに関連付けられる形で配
列として格納されてるっぽいので、まあ黒魔術的なものではあるよね」
「……えっ?」
「useState() を極端に単純化すると、おおむねこんなコードになるはず」
111
第8章 Hooksで関数コンポーネントを強化する
let currentIndex = 0;
const states = [];
return [
states[index] || initialState,
newState => { states[index] = newState; }
];
}
列の順番がおかしくなるので、それはタブーになってるの。関数定義の最初のほうにこんなふうに
userState() をプレーンに連ねて書くこと」
「わかりました。でもなんか、さらっとすごいことを流された気もしますが、Hooks 確かに便利だし、
あまり気にせず使うことにします」
「ふふ。そう、それが賢い選択よね」
112
8-4. Effect Hookでライフサイクルを扱う
のになる。使い方はこんな感じ」
useEffect(() => {
doSomething();
return clearSomething();
}, [watchVar]);
「useEffect() は第一引数に、引数なしの関数を設定します。その渡した関数の中身、ここでは
doSomething() が コ ン ポ ー ネ ン ト の レ ンダ リ ング の 直 前 に 実 行 さ れ る こ と に な る の 。
ずしも戻り値を必要としないけど、戻り値に関数を設定すると、それはコンポーネントのアンマウン
useEffect() の第二引数は配列で指定する必要がある。これは省略可能なんだけど、その配列の中
に任意の変数を入れておくと、その値が前回のレンダリング時と変わらなければ第一引数で渡された
関数の中身の副作用処理実行がキャンセルされることになるの」
で合ってますか?」
「うん、100 点満点」
「ちなみにですけど、ここで第二引数を省略したり、または空の配列を渡したりしたらどうなるんで
すか?」
「お、いい質問だ。第二引数を省略した場合は問答無用でレンダリングの毎回に doSomething() が実
行される。空の配列を渡してあげると、doSomething() は初回のレンダリングでしか実行されなくな
る」
「なるほど、componentDidMount() と componentDidUpdate()のふたつあったものがまとめられちゃっ
113
第8章 Hooksで関数コンポーネントを強化する
て、初回のレンダリングしか実行したくない処理があったときはどうするんだろうと思ってましたけ
ど、空の配列を第二引数に渡してあげればいいんですね」
「せっかくおぼえたコンポーネントのライフサイクルの考え方だけど、Hooks ではそのパラダイムを
少し変換する必要があるんだよね。クラスコンポーネントで書いていたときは『このライフサイクル
のタイミングでこの処理とこの処理を実行する』と考える必要があったんだけど、Effect Hook で書
くときは『この処理を実行したいのはこれとこれのライフサイクルのタイミングだ』と発想が逆にな
るの」
「お――、なるほどー。確かにこっちのほうが綺麗に書けそうですね」
「わかりました」
き直してみるよ。クラスコンポーネントで実装した減算タイマーのサンプル*7 を書き直したもの*8 を用
意したから、src/App.tsx を開いてみよう」
import './App.css';
*7 7-4. コンポーネントのライフサイクル
https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/07-component/04-lifecycle
*8 https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/08-hooks/04-effect
114
8-4. Effect Hookでライフサイクルを扱う
useEffect(() => {
const timerId = setInterval(tick, 1000);
return (
<div className="container">
<header>
<h1> タ イ マ ー </h1>
</header>
<Card>
<Statistic className="number-board">
<Statistic.Label>time</Statistic.Label>
<Statistic.Value>{timeLeft}</Statistic.Value>
</Statistic>
<Card.Content>
<Button color="red" fluid onClick={reset}>
<Icon name="redo" />
Reset
</Button>
</Card.Content>
</Card>
</div>
);
};
setInterval() の と こ ろ の 処 理 は 初 回 の レ ンダ リ ング で し か 実 行 さ れ な い 。 戻 り 値 と して
clearInterval() の実行関数を設定しているので、コンポーネントがアンマウントされる際にそれが
実行される」
る処理はどこに行ったんですか?」
115
第8章 Hooksで関数コンポーネントを強化する
の中では古い値しか参照できなくてそこにも置けなかったので、setTimerLeft() の引数に渡す関数の
中に入れちゃったんだよね」
るところですね。そうか、こうすればいいんですね」
「まあ、苦しまぎれの気もしなくないけど、他に書きようがなかったので」
「Hooks、すっきり短く書けるというメリットばかりじゃなく、ライフサイクルメソッドが使えないた
めにそういうところもちゃんと考えて設計する必要もあるんですね」
「そうだね、こうやって思わぬ挙動になることがあるから。その必要もないのに既存のクラスコンポ
することが簡単にできるようになる。さっきの減算タイマーをそのやり方で書き換えてみよう 」
*9
src/containers/App.tsx のふたつに分割されていますね」
「コンポーネント
(Presentational Component)
とコンテナ
(Container Component)
に分けたからね。
React
アプリケーション開発でのディレクトリの切り方についてはいろいろ流派があるみたいだけど、
数派のようなので、ウチでもそれを採用してるの。
*9 https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/08-hooks/05-custom
116
8-5. Custom Hookで独自のHookを作る
ではまず、src/components/App.tsx のほうから見ていこうか」
import './App.css';
interface AppProps {
timeLeft: number;
reset: () => void;
}
うにしてるの。これをこのままマウントすれば、見た目だけ整ってるけどタイマーが全く機能しない
コンポーネントが表示される。スタイルガイドに載せるのはこういったコンポーネントだね」
「これだけシンプルなコンポーネントなら関数定義の中でついでに定義してもいいんだけど、複雑な
アプリになってくるとこうやって切り出した方がいいだろうね。HOC を使うシーンだと、コンテナ
のほうでこれをインポートして拡張することも多いし」
117
第8章 Hooksで関数コンポーネントを強化する
「へー、そうなんですね」
useEffect(() => {
const timerId = setInterval(tick, 1000);
「この useTimer() というのが自分で作った Custom Hook だね。変数 timeLeft と関数 reset() を返す
ようになってる。中身の処理は、さっきのサンプルコードと全く同じなんだけど」
「へ―――、綺麗にカプセル化されてますね」
複数のコンポーネントで使い回すことも可能だよ。ここでもある程度はそうしてるわけだけど。
118
8-6. その他のHooks
「お、すごい」
うなっているだけで、ESLint を外せば動くんだけど」
ってピンとこなかったんですけど、こんなふうに書くんですね」
「分離することで全体のコード量は多少増えてしまうけど、それよりもコンポーネントの独立性を高
めて再利用性やテスタビリティを担保することのほうが、長い目で見れば断然重要だからね。秋谷さ
んも早くこのやり方に慣れてほしいかな」
「わかりました! がんばります!」
「Hooks を使う際の注意点のおさらいをしておこうか」
や React モジュール管轄外での使用は不可。
・ Hooks 文を記述するのはその関数のトップレベルで行う。条件分岐やループ、ネストした関
数内に記述するのは不可。
「はい、だいじょうぶそうです」
119
第8章 Hooksで関数コンポーネントを強化する
はたくさんある。
・ useState
・ useEffect
・ useContext
・ useReducer
・ useCallback
・ useMemo
・ useRef
・ useImperativeHandle
・ useLayoutEffect
・ useDebugValue
「えっ、こんなにあるんですか?」
らいかな。
の current プロパティの値は変更可能でどんな値でも保持することができるので、インスタンス変数
かは、こんなふうに書ける」
useEffect(() => {
prevCountRef.current = count;
});
120
8-6. その他のHooks
};
いうものだと思っておぼえましょう。
く、任意の計算結果を保持しておきたいときに使う。こんな感じ」
「 第二引数の配列に渡された変数が前回のレンダリング時と差分があれば、第一引数の実行結果が戻
り値として返されるの。
また useMemo() はパフォーマンス最適化のためによく使われることがあるんだけど、たとえば特定
の Props が変更されたときだけ任意の子コンポーネントの再レンダリングを行いたい場合はこんなふ
うに書ける」
return (
<>
{child1}
{child2}
</>
)
};
すよね?」
「そう、その通り。shouldComponentUpdate() とちがって、マウントする親のコンポーネントにロジ
ックを書かないといけないのが面倒だけどね。ちなみにパフォーマンスの最適化については、コンポ
121
第8章 Hooksで関数コンポーネントを強化する
ーネント設計の初期段階で行うのはタブーなので、完成したもののパフォーマンスがどうしても出な
かったときに改めて検討するようにしましょう。
てもらえたかな?」
「そうですね。すごく便利そうということはわかりました。でもこれ、まさにかなり頭のいい人が考
えた機能って感じですよね。はまれば超シンプルで綺麗に書けるのはわかるんですけど、凡人の私が
使いこなせるようになるには時間がかかりそうです……」
「まあそのへんは、何ごとも最初は実践による試行錯誤を繰り返していくしかないから。基本的な考
え方はインストールしてあげたつもりなので、あとは自分でコードを書きながら、また綺麗に書いて
る他の人のコードを読んだりしてスキルを上げていくしかないね」
122
9-1. SPAのルーティング
「じゃあ秋谷さん、Web アプリケーションにおける『ルーティング』の定義を述べてください」
「……いきなりですね。えーっと、
『アプリケーションサーバがリクエストされた URL に対して、そ
れに紐付けられたページを生成し、レスポンスとして返すこと』でいいでしょうか?」
とがあっても、サーバにリクエストが飛ぶことは原則的にないの」
「そうなんですね。そのへんの理解はあいまいでした」
たりする場合はその限りではないけどね。ちなみにブラウザでこんなことができるようになったのは
を見たことはない?」
「うーん、見た記憶はないですね……」
つけるとページ内リンクアンカーになって、そこ以降が変わってもブラウザの履歴は変わるけどサー
バにリクエストは飛ばないでしょ。それを利用したわけ」
「へえー、豆知識ですね」
123
第9章 ルーティングでURLを制御する
るんだよ」
じゃあ逆に、サーバーサイドフレームワークでのルーティングとの違いって他にありますか? 開発
する上で気をつける点とか」
「サーバにリクエストが行かないということは、サーバ側からはクライアントがどんなページを見て
るかとか、ページをどう移動したかとかがわからないわけだよね。これはアクセス解析を行う上でネ
で対処できるよ。
でフォローする必要がある。
他には、ルーティングの適用単位がコンポーネントだというのも前提知識として知っておく必要が
パーツテンプレートの出し分けはできるけど、React に対応したルーターでは親コンポーネントのあ
「なるほど。
『React はコンポーネント指向』って、そういうことでもあるんですね」
*1 https://github.com/react-ga/react-ga
124
9-2. React Routerにまつわるあれこれ
Router 一択だろうね。
ただ気をつけてほしいのは、今の最新版はバージョン4系なんだけど3系のほうもまだ開発が続け
「えっ、それどういう状態なんですか?」
というルーティングから描画コンポーネントに処理を渡す直前に任意の関数を実行させる属性値があ
レンダリングが実行されたりといった強力な機能があったんだけど、それらが4系になって根こそぎ
削除された」
「一見便利そうですけど、どうしてなくしちゃったんでしょう?」
「React 本体と関係ないところでレンダリングをブロックしたり別の処理やレンダリングを差し込む
のは、そのライブラリとして行儀がいい振る舞いとは言えないからね。これらの機能は使う人によっ
てはありがたいのかもしれないけど、そういった処理はライフサイクルメソッドやコンポーネントの
は便利かもしれないけど、そのコードを読まされるほうは追わなきゃならない流れが幾重にも複雑に
の機能をごっそり削ったんだと思う」
「なるほど。じゃあ迷わず最新版の4系を使うべきなんですね?」
「そうだね。あともうひとつ、使うのを避けたほうがいいモジュールを紹介しておきます。
*2 https://reacttraining.com/react-router/
*3 https://www.kriasoft.com/universal-router/
*4 https://github.com/reacttraining/react-router/tree/master/packages/react-router-redux
125
第9章 ルーティングでURLを制御する
「公式なのに使わないほうがいいんですか?」
「Redux についての説明は後でくわしくやる予定だけど、必要なのでちょっとさわりだけ説明してお
ろに書けるんだけど、react-router-redux はその処理中にリダイレクト処理とかを差し込むことができ
ましくない。React アプリは全てがコンポーネントで構築されているはずなので、リダイレクトが必
思うよ」
「ちょっと今の私には難しいですけど、ルーティングにおいてもコンポーネント指向を徹底するべき
ということですよね」
「端的に言えばそういうことだね。
発していて、自社のサイトでいくつかのサンプルを含んだ詳しいドキュメント*5 を提供しているのでそ
れを見といてね、で終わるんだけど……」
「えええー、そんな投げやりな。ちゃんと教えてくださいよー」
「はいはい、ちゃんと基本的な使い方は教えます。でも全てはカバーできないので、ふだんから自分
で公式ドキュメントを読んでおく習慣をつけておくこと。いいエンジニアになるために大事なことだ
からね」
「……はーい、わかりました。あとで余裕のあるときに目を通しておきます」
定 なので、それが出たら
*6
*5 https://reacttraining.com/react-router/web/guides
126
9-3. React Routerの使い方
「今回のサンプル*7 は、関数コンポーネントの説明のときに使った『はねバド!』のキャラ紹介のコ
ードに大幅に手を加えたものになります。ちなみに新しくインストールしたライブラリは以下の4つ
ね」
・ react-router
・ react-rouer-dom
・ @types/react-router
・ @types/react-router-dom
*7 https://github.com/oukayuka/ReactBeginnersBook/tree/master/09-routing/03-react-router
127
第9章 ルーティングでURLを制御する
「ホームページと高校別のキャラクター一覧ページがあって、それぞれリンクされてますね」
import './index.css';
128
9-3. React Routerの使い方
import './styles/semantic.min.css';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root'),
);
serviceWorker.unregister();
の機能が使えるようになるわけ」
「ふむふむ」
import './App.css';
ントごとにルーティングできるんだけど、単純化のために今回はほぼページ全体をひとつのルーティ
129
第9章 ルーティングでURLを制御する
ングの対象にしてる」
したコンポーネントがレンダリングされるわけですか」
130
9-3. React Routerの使い方
</List>
</>
);
しておいて。characerData.kitakomachi.players[0] みたいにアクセスできるようになってる」
interface Character {
id: number;
name: string;
age: number;
height?: number;
}
トヘッダが上書きできるようになるの。こうしないとどのページに行っても public/index.html に書
「なるほどー、これも便利なモジュールですね」
131
第9章 ルーティングでURLを制御する
の名の通りリンクを表示するコンポーネントだね」
ドされるわけだから」
「あっ、そうか。そうなるんですね……」
「じゃ、最後。src/components/Characters/index.tsx を開いて」
import './index.css';
return codes.includes(targetCode) ? (
<>
<Helmet>
<title> キ ャ ラ ク タ ー 一 覧 | は ね バ ド ! </title>
</Helmet>
<header>
<h1> は ね バ ド ! キ ャ ラ ク タ ー 一 覧 </h1>
</header>
{isLoading ? (
<Spinner />
):(
<CharacterList
132
9-3. React Routerの使い方
school={characterData[targetCode].school}
characters={characterData[targetCode].players}
/>
)}
<Divider hidden />
<Button basic color="grey" onClick={() => { history.push('/'); }}>
<Icon name="home" />
ホームへ
</Button>
</>
):(
<Redirect to="/" />
);
};
小町高校の、http://localhost:3000/characters/furejo のときにフレ女の登場人物一覧に振り分け、
ンポーネントになってるでしょ?」
「たしかに」
133
第9章 ルーティングでURLを制御する
しょうか?」
て ホ ー ム に リ ダ イ レ ク ト さ れ る ん で す ね 。 試 し に ア ド レ ス バ ー の URL を
http://localhost:3000/characters/konan に書き換えたら、やっぱりリダイレクトされました!」
換えてみて」
「あれ? 『読み込み中...』ってぐるぐるローダーが回って中身が表示されなくなりました」
134
9-3. React Routerの使い方
るわけ。query-string というのはクエリーパラメータを扱うためのモジュールね。
という無名関数が渡されてるよね」
「<Redirect> コンポーネントの描画でもリダイレクトができてましたけど、こう書く方法もあるんで
ないので、違いさえ認識していれば頭に入りやすかったです!」
「そう、よかった。でもサーバーサイドと違ってあと一点、注意が必要なことがあるのでそれについ
の挙動はそう」
「?? ちょっとピンとこないです」
「そうだね。ちょっとブラウザでこの技術書典5のサークル紹介ページ*8 を開いてみて。それからペ
ージの一番下の『次のサークル』ってリンクをクリック」
「えっ? 一番下にスクロールされた状態のまま、新しいページに飛んじゃいました。一瞬、何が起
こったのかわからなかったです」
ーションなら、ページをまたぐと当然のように毎回トップ位置から始まるわけだけど、History API
の pushState() を使ったルーティングでは履歴が変わって新しいページがレンダリングされても、ス
クロール位置は遷移前のままになる。これは一般ユーザーの期待する挙動じゃなくて、かなり違和感
を抱かれるよね」
「それはもう、そうですね」
んだコンポーネントを作って、DOM ツリーの上のほうでマウントさせるみたいな処理が必要になる。
*8 https://techbookfest.org/event/tbf05/circle/27720001
135
第9章 ルーティングでURLを制御する
とりあえずおぼえておいて」
「は――、やっぱりサーバサイドアプリケーションとは勝手がけっこう違うんですね」
136
10-1. Fluxアーキテクチャ
第 10 章 Redux でアプリの状態を管理する
「React ではコンポーネントを組み合わせてアプリケーションを作っていくわけだけれども、コンポ
ーネントには状態を持たないステートレスなコンポーネントと、状態を持つステートフルなコンポー
ネントがあるってことがここまででわかったよね? ただ実際のアプリケーションには、コンポーネ
ントをまたいで保持したい状態が存在することがよくある。たとえばユーザーのログイン・非ログイ
ン状態やアカウント情報なんかがその最たるものだね」
「はい、単方向データフローの説明のときにも出た話ですよね。React ではどうやって管理してるん
でしょうか」
「ひとつ考えられる方法としては、上位のコンポーネントに必要な状態を全て持たせておいて、それ
「いやそれ、めちゃくちゃ大変じゃないですか……?」
「あはは。まあ、そうだね。さすがに今はそんなことをしてる人はいないだろうね。もうひとつは React
用途でしか使われてるのを見ないかな」
「へー、そうなんですね」
しかないので、そういったアプリ全体の状態管理を含めたアプリケーションのアーキテクチャをどう
137
第10章 Reduxでアプリの状態を管理する
レーをしたり、他のフレームワークを組み合わせて使っていたみたいだけど、React の急速な普及に
より、その性質に合った新しい設計指針が求められるようになってきたの。
ぎになったらしいね」
たんだけどね。
『Model と View の間に双方向のデータフローが作られるため、新しい機能を追加しよ
うとするたびに、システムの複雑度はが指数関数的に増大し、そのコードは《壊れやすくて予測不能
なもの》になってしまう』というのが彼らの言い分。一枚岩の静的ページを返せばいいサーバーサイ
面が出てきてしまうからね」
「なるほど」
らおうかな」
138
10-2. Reduxの登場
「Store とはとりあえずアプリケーション全体で保持するべき情報を蓄えたものと考えて。View から
によって処理され、Store の中身が書き換えられる。そういうアーキテクチャなの。
ーが常に単方向になることが保証され、どんな複雑なシステムでも《破綻しにくく予測可能なもの》
にすることができる。そう、単方向データフローだね」
Facebook による実装のこちらを指すこともあるのでまぎらわしいね」
「はー、そうなんですね」
参加していた Yahoo!といった大企業でもない、在野の一開発者が公開したあるプロダクトが、登場す
「おおお、ドラマチックですね! ……にしても、ここでもまた公式が負けちゃってるんですね」
*2 http://facebook.github.io/flux/
139
第10章 Reduxでアプリの状態を管理する
んだけど、彼は文章の才能もあってドキュメントやブログ記事の説得力がすごくて、いっそうその普
套戦略になってるよね」
「でも彼らのあいだに具体的にどういういきさつがあったか、気になりますね」
どセットとして扱われるようになってるけど、そこに至るまでにはこんな経緯があったんだよ。
があるの。それがこれらね」
・ State is read-only(状態は読み取り専用)
取りが面倒になるよね。ひとつのオブジェクトに集約されることで、デバッグやテストもやりやすく
なる。わかりやすく他にたとえるなら、どこで定義されているかわからない複数のグローバル変数に
依存したアプリケーションを想像してみて」
「それはふつうに嫌ですね……」
そしてふたつめの『State is read-only』
、これは View やイベントのコールバックが Store の状態を直
140
10-2. Reduxの登場
かじめ定義された純粋関数によって行われるということ。Reducer は簡易化するとこんな式で表現さ
れる関数になるの」
「古い状態を引数に取り、新しい状態を返す関数ですか」
デルに組み込んで表現したのが以下の図ね」
だけど、科学の分野では『還元する』
『減数分裂させる』という意味もある。(prevState, action) =>
newState の式って、まさにそのイメージだよね。ちなみに『Redux』って名前もここから由来してる
141
第10章 Reduxでアプリの状態を管理する
んだよ」
「へえ、なるほどー。そうだったんですね」
ている。だから動作の信頼性が高いし、テストやデバッグもやりやすい。そんな点が開発者たちの心
をつかんだんだろうね」
「ふむふむ。納得です」
け普遍的な思想を持ったプロダクトだってことだね。
ト を読んでみることをおすすめするよ」
*3
「うう、また公式ドキュメントですか……。英語ですよね……」
「秋谷さん、英語は得意だったでしょ?」
「いや、得意ってわけじゃ……。勉強はしてますけど」
開発の動機から、さっき説明した三大原則、影響を受けた思想・技術について説明があっておもしろ
「……わかりました。そこの部分だけでも読んでおきます」
れていて、そのおかげでパフォーマンスが大幅に改善しているみたいね。そのリリースノートの中で、
*3 https://redux.js.org/
142
10-3. Reduxの使い方
かになるって言及してたので、直近ではなくてもそう遠くない将来では使えるようになってそう。で
を改造してみたんだけど 、インストールして実行してくれる?」
*4
「はい、実行……と。わぁ、今回のはカウント数に合わせて色とりどりのビーズが表示されるんです
ね。キレイ」
「うん。カウンターのボックスとその下のビーズ表示枠は対応するコンポーネントが並列の階層にあ
*4 https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/10-redux/03-redux
143
第10章 Reduxでアプリの状態を管理する
src/components/Counter.tsx を開いてみよう」
import './Counter.css';
144
10-3. Reduxの使い方
);
「これはスタイルガイドとかに載せるとき、Props をいちいち指定しなくてもマウントできるようにし
ておいてるんだよ。ただの変数だけならともかく、マウントするために毎回ダミーの関数を渡してや
る必要があるのもわずらわしいし」
「なるほど」
「あとはだいじょうぶそうね。色とりどりのビーズが表示される部分に対応してるコンポーネントは
src/components/ColorfulBeads.tsx なんだけど、いちおうそこも見ておこうか」
import './ColorfulBeads.css';
const range = (n: number) => (n < 0 ? [] : Array.from(Array(n), (_, i) => i));
const colors: SemanticCOLORS[] = [
'red', 'orange', 'yellow', 'olive', 'green', 'teal', 'blue',
'violet', 'purple', 'pink', 'brown', 'grey', 'black',
];
145
第10章 Reduxでアプリの状態を管理する
「ビーズを表示するところで配列の繰り返し処理を行うので、たとえば長さが 4 なら [0, 1, 2, 3] と
range() というメソッドがあるけど、わざわざこのためにインストールするまでもなくこうやって一
行で書けるので、頭のところで定義してるの」
きは空オブジェクトを返してるのか」
ードになる。src/actions/counter.ts を開いてみて」
*5 https://lodash.com/
146
10-3. Reduxの使い方
か?」
ていくんだけど、type が文字列だったりしたらうっかりタイプミスとかで想定の動きと違ってしまう
ことがあるので、その予防措置だね。ではその振り分けの部分を次に見てみよう。src/reducer.ts ね」
return state;
}
}
};
147
第10章 Reduxでアプリの状態を管理する
「お、switch-case 文ですね」
しないように必要だけど」
「おっ、いいところに気がついたね。まさにその通りなんだけどね。ただ実際の業務開発では State
の中身が一要素だけってことはほとんどないし、後から要素を追加したときに書き忘れて他の要素が
上書きされるってケースもないわけじゃないから、あえてこうしてあるの。
る。その他に聞いておきたいところはあるかな?」
なんですけど、これってどういうことですか?」
あって、そのままだと型エラーでコンパイルが通らないの。undefined は真偽値で言えば偽だから、
こう書いておけば || の論理演算式でうしろの値が評価されて0が代入されるってわけ」
「なるほど―――」
*6 4-2. 型のバリエーション
148
10-3. Reduxの使い方
import './index.css';
import './styles/semantic.min.css';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root') as HTMLElement,
);
serviceWorker.unregister();
きるようになるわけ」
「ふ――ん、そうなってるんですね」
interface StateProps {
count: number;
}
interface DispatchProps {
add: (amount: number) => void;
decrement: () => void;
increment: () => void;
149
第10章 Reduxでアプリの状態を管理する
「そうだね。ここでまずやりたいことは、
『参照したい Store State の値をコンポーネントの Props に
DispatchProps として定義してるの」
RouteComponentProps を合成してました」
させてるの。これは bindActionCreators()というユーティリティ関数を用いてこう書くこともできる
よ」
150
10-3. Reduxの使い方
プロパティ名をショートハンドで省略できるの」
「こっちのほうがシンプルに書けていいですね」
「うん。それでもこのへんの書き方はややこしいので、お約束と思って丸暗記しちゃったほうがいい
かも。
こ れ ら ふ た つ の 関 数 の 名 前 は 本 当 は 何 で も い いん だ け ど 、 慣 習 的 に mapStateToProps と
mapDispatchToProps と書かれることになってるね。まあ、そのまんまの意味でわかりやすいからね。
け」
「connect、引数のカッコがふたつあるということは、これカリー化された関数なんですね」
「そう、これもややこしいのでそういうものとしておぼえときましょう。そしてここでエクスポート
分で見ておいてね。
「ふ―――、やっと終わったー! ありがとうございました。でも、柴咲さんの順を追っての解説付
きだったから何とかわかった気になりましたけど、自分でこれを一から書けるようになる自信があり
ません……」
「あはは。そうかな、そうかも。でもそれを言うなら、私だって難しいよ。だいたいどこかのボイラ
ープレートを使わせてもらったりとか、他のプロジェクトで書いたコードを再利用してるもの」
「えっ、そうなんですか?」
151
第10章 Reduxでアプリの状態を管理する
「バックボーンを理解していれば書かれたものは読みやすいし、テストやデバッグも容易だけど、自
げでだいぶ手軽になったけど、Redux にはそういうのがないからねー。誰か作ってくれないかな」
「それを聞いて安心……しちゃいけないんでしょうが、ちょっと気持ちが楽になりました」
始めとする様々な技術が生まれて、激しい競争の中で優れたものが残り、今も進化し続けてるとも言
えるわけだしね」
「なるほど、一長一短なんですね。でも技術トレンドの先端でバリバリ活躍してるハッカーたちなら
心惹かれるものなんでしょうけど、私みたいな初心者にはやさしくないです……」
よ。でも一度理解してやり方をおぼえてしまえばメリットのほうが全然上回るんだけど、初見でひる
くらいだし」
「あらら、Redux の作者がこんなこと言っちゃっていいんですか?」
「ええっ?!」
152
10-3. Reduxの使い方
「
『絶滅計画』って……」
と言ってる。
でも業務で開発するアプリに、コンポーネントをまたいで共有したいデータがないなんてことはま
「確かにそうかもしれませんね」
「背景となった歴史やその思想、そしてその技術がもたらすメリットをきちんと理解することもない
まま、短絡的に特定の技術をディスる開発者が多くて、私はつねづね憤慨しているのですよ」
「……なんだかすみません。でも雪菜さんにここまでていねいに教わったので、だいじょうぶです!
「
『雪菜さん』?!」
「いえ、もうこれだけ毎日顔を合わせてお話ししてるわけですから、そろそろ名前で呼ばせてもらっ
てもいいころかなーって、ダメでしたか?」
「……いや、別にダメってことはないけど……」
「やった! じゃあこれからは『雪菜さん』で。あ、私のことも名前で呼んでいいですよ?」
「ん――、遠慮しとくわ。私は仕事とプライベートはきちんと分けたいので」
「えええー? でも私、プライベートでも雪菜さんと仲良くなりたいと思ってるんですけど……」
「………………」
「そんなあからさまに引かなくても。じゃあこの先の課題と言うことで。考えといてくださいね」
*7 https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367
153
第10章 Reduxでアプリの状態を管理する
れていて、Redux の動作に介入して様々なことをさせることができるミドルウェアが手軽に作れる仕
組みを提供しているの。
ただ、明確な決まりごとがない中で開発者がそれぞれのマイルールで思うままに開発すると、こち
らのミドルウェアで動いていたものがあちらのミドルウェアでは動かない、なんてことが起こったり
interface CounterAction {
type: CounterActionType;
amount?: number;
}
mapDispatchToProps() で型エラーが起きる」
「あ、ほんとだ」
*7 https://github.com/redux-utilities/flux-standard-action
154
10-4. Flux Standard Action
ミドルウェアの動作保証のためにもこれに準拠した書き方にしといたほうがいいということ。それに
Action の型のフォーマットをあらかじめ決めておくことは、開発チームのメンバーが増えたときに混
乱を防ぐことにもつながるし」
「ふむふむ、なるほど」
interface Action {
type: string;
payload?: Object;
error?: boolean;
meta?: Object;
}
「各プロパティについて説明するとこんな感じ」
きない必須プロパティ。
する。オブジェクトで表現される。省略可能なプロパティ。
ことを意味する。省略可能なプロパティ。
・ meta …… その他追加で必要な情報を格納する。オブジェクトで表現される。省略可能なプ
ロパティ。
155
第10章 Reduxでアプリの状態を管理する
に準拠してないってことですね。amount というプロパティは定義されてませんから。この場合は
payload に入れるのが適切なんでしょうか?」
interface CounterAction {
type: CounterActionType;
payload?: { amount: number };
error?: boolean;
meta?: object;
}
になったりしたんだよね」
「へえー、そんな苦労があったんですね」
する独特の書き方になってしまって気持ち悪い。つまりそれは、そのライブラリを外すためのコスト
*8 https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/10-redux/03-redux/src/actions/counter.ts
*9 https://github.com/piotrwitek/typesafe-actions
*1 https://github.com/aikoven/typescript-fsa
156
10-4. Flux Standard Action
も高いということでもあるよね」
「ふむふむ、確かに」
「あらら……」
「仕方なく、メンテナンスが必要なコードは全て書き直しになった。この痛い経験から、改めて
typesafe-actions のような別のユーティリティを使う気にはなれなかったな。そしてその間にも
るようになってたの。開発者コミュニティのトレンド的にも、ユーティリティを使わずに最新の
「ほほぅ、そのへんのお話は参考になります」
るという逆のアプローチになるので。具体的にはこんなコードになる」
157
第10章 Reduxでアプリの状態を管理する
| ReturnType<typeof increment>;
えるこの書き方って何なんですか?」
されてしまうので、そうじゃなくてここは共用型に使われる文字列リテラルなんだよって注釈だね。
はこんな書き方もできるようになるよ」
に強制するのは難しい。ためしにやってみたんだけど、型推論を有効にしたまま実行させるのは難し
かったし、できたとしても一見さんにはひどく難解な型プロレスになりそうだった。それを避けるた
めには『FSA 準拠をやりたいから気をつけて書いてね』とチームメンバーにお願いする、という性善
説に則るしかなくなるわけだけど、前述したような状況を踏まえてメリットとデメリットを考えると
現状はこの形で妥協しておくのがベターかなというのが私見だね」
「うーん、難しいところですね……」
を書くときはこの型を意識して書いてね、ということで」
158
10-4. Flux Standard Action
「うん。最初のはノーマルな Action。あとの3つは、非同期処理を行うときの「開始/成功/失敗」
ま持ってきたので、汎用性は高いと思う」
Reducer とかもどう変わったか見ておいてね」
「はい、わかりました。でもそれにしても、今回に限らず横で雪菜さんがコーディング中に素早く型
を解決していくのを見て、
『えええー? 私にはこんなの無理ー!』と気後れしちゃいます……」
「まあこのへんの型の整合性のとり方は、単純に経験というか慣れかな。赤の破線アンダーラインが
*11 https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/10-redux/04-fsa
159
第10章 Reduxでアプリの状態を管理する
ラーメッセージで教えてくれるから、それを最後まで読んで理解しようと努力する。それから指摘さ
れた型の定義元のファイルを開けば、型や引数の名前でだいたいの目星がつくから、それに合わせて
コードを修正していく、みたいにトライ&エラーをやっていくわけ」
「お、おう……」
してるコードがあるはずだから、それを見れば正しい引数や型引数の渡し方の参考になるよ。私はこ
れでかなり助かってる。でも、私も最初のころは型の不整合にハマってうなりながら半日つぶすとか
ザラだったし、まあそんなもんだよ。秋谷さんの場合は私があげたサンプルコードがあるんだから、
ずっと楽なはずだよ」
「……そうですね。言われてみればエラーメッセージとか、ちゃんと読んでなかった気がします。今
言われたこと、次からやってみます!」
160
11-1. Reactで非同期通信を扱ういくつかの方法
第 11 章 Redux で非同期処理を扱う
「そうですよね。今のままではローカルなデータしか持てないので、Web アプリケーションとして一
般ユーザーの使用に耐えられるようなものが作れません」
れたときから様々な方法が試されてきた。まずは大きく分けてふたつの方針に則った方法がある。
だね」
「ふむふむ」
「コンポーネント内で処理するというのは、async/await などを用いて非同期通信処理を行う関数をま
「なるほど。それならこれまで勉強してきたことで想像つきますし、実際のコードもすぐに読み解け
そうです」
「うん、でもこの方法を採用してるケースは実際には少ないの」
「えっ、どうしてですか? 考え方が素直だし、シンプルに書けそうなのに」
「コンポーネントと通信処理が密結合になってるっていうことは、コンポーネントのコードが複雑に
入り組んでしまい、それが可読性やテスタビリティの低下につながりやすい。React 開発者は特にそ
のふたつを嫌うからね。さら他のコンポーネントからそのデータを取り回して再利用するのが難しか
ったり、アプリ全体で見たときにどこで通信が発生しているのか把握しづらかったり、といった問題
もある」
「……うーん、そう言われると問題な気もしてきました」
161
第11章 Reduxで非同期処理を扱う
というものがあるんだよね。2019 年の中期にリリースされる予定になっていてまだ詳細は不明なんだ
けど、今挙げたようなポイントをクリアできないようなら、この技術も不発に終わりそうな気がして
る」
「例の、React 公式が出してくる技術の打率の低さですね……」
使う方法なの。以前、Redux は単純な状態管理しか行わないけども拡張性に優れていて、その動作に
介入して様々なことをさせることができるミドルウェアが手軽に作れる仕組みがあるって話したよね」
「はい、おぼえてます」
「わ、たくさんありますね」
「マイナーなものも含めるともっとあるよ。ちなみに4つのミドルウェアのダウンロード数を npm
trends で比較したグラフがこれね 」
*6
*1 https://reactjs.org/blog/2018/11/27/react-16-roadmap.html
*2 https://github.com/reduxjs/redux-thunk
*3 https://redux-saga.js.org/
*4 https://redux-observable.js.org/
*5 https://github.com/pburtchaell/redux-promise-middleware
*6 https://www.npmtrends.com/redux-thunk-vs-redux-saga-vs-redux-observable-vs-redux-promise-middleware
162
11-2. Redux Thunk vs. Redux-Saga
「雪菜さんが使ってるのはどれなんですか?」
「へ―――、どうして乗り換えたんですか?」
163
第11章 Reduxで非同期処理を扱う
(prevState, action) => newState の純粋関数で表される Reducer によって Store の State が更新される
というのが、この図が示す内容になるわけだけど」
「はい、ここは一度やったのでだいじょうぶです」
164
11-2. Redux Thunk vs. Redux-Saga
所と短所を列挙してみるとこんな感じになる」
Pros
・ 概念が理解しやすく、学習コストも低い。
・ コード記述量も比較的少ない。
・ Redux 公式チームが開発しているため、メジャー感があって使用実績が圧倒的に多い。
165
第11章 Reduxで非同期処理を扱う
Cons
しまう。
の本来のあり方を変えてしまう。
すい。
もコードが汚くなる』と言ったところじゃないかな。そういえば前にも少しふれたよね。私の前の職
「ひいっ」
「Thunk 派の人たち的には、
『気をつけてればそんなにコード汚くなんてならないよ』って言うんだ
けど、普通の人たちが集うチームだと『なるよ! なるから!』としか私は言えない。経験上ね。
「あはは、そのへんはさすが雪菜さんらしいですね」
うになるまでのハードルは多少高かったけど、その学習コストを払うだけの価値はあった」
もなう非同期処理を『タスク』として登録しておく。アプリが起動するとタスクの数だけ独立したス
166
11-2. Redux Thunk vs. Redux-Saga
「……うーん、ちょっとわからなかったです」
「えっ、登場人物多くないですか?」
図とほとんど同じでしょ?」
Task にもパススルーしてるんですね」
167
第11章 Reduxで非同期処理を扱う
して任意の処理を実行させるの」
「……なんとなくイメージがつかめてきました」
べると、それが象徴されているのがよくわかるよ」
分的に絡んでるように見えますね」
ションを実現するための解決策として提案されたデザインパターンのこと。複数のサービスにまたが
るリソースに対するローカルなトランザクション処理を、イベントをトリガーにして複数連ねて実践
していくものなの」
「さっきのタスクの話ですね」
*7 SAGAS https://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf
168
11-2. Redux Thunk vs. Redux-Saga
「この一連のワークフローのことを『Saga(物語、神話)
』と呼ぶ。また Saga は処理の進行状況を表
す状態と、各サービスが返すドメインのデータのふたつの状態を持つことも特徴になってる。後者は
始/成功/失敗」の3つの状態を持たせてこれを表現することになる」
「んんー、難しいですね」
「まあこのへんの説明は全部は理解する必要はなくて、バックグラウンドとして理解の助けになれば
いいかな程度だから。マイクロサービスの文脈から提案されただけあって、ドメインをまたがって複
Pros
ビリティが高くなる。
・ 非同期処理を同期的に書くことができ、コールバック地獄に陥りにくい。
・ 開発が活発。
Cons
・ 概念が理解しづらく、学習コストが高い。
・ 必要なコード記述量がどうしても多くなる。
る。
「Thunk の総評が『理解しやすく導入しやすいけど、放っておくとどこまでもコードが汚くなる』だ
ったけど、Saga は『綺麗に書けるけど、学習コストが高くてコード量も増える』といったところかな」
「まさに雪菜さんが好きそうな技術ですけど、難しいのは私は苦手です……」
「だーいじょうぶ。まーかせて。ちゃんと今からわかるように説明してあげるから」
「不安だなあ……。あ、ちょっと気になったのが『開発が活発』ってところなんですが」
169
第11章 Reduxで非同期処理を扱う
「Thunk は中身がシンプルだからその必要がないのかもしれないけど、中身は3年前からほぼ変更な
「Saga は玄人受けするからなんだろうね。それだけ乗りこなし甲斐のある技術ってことだよ」
「うう、難しそうだけど、がんばってチャレンジしてみます!」
「これは何でしょうか?」
ページネーションとかにまで話が広がると混乱するので、とりあえずそこで区切ってる」
*8 https://github.com/oukayuka/ReactBeginnersBook-2.0/tree/master/11-async/03-saga
170
11-3. Redux-Sagaを使いこなす
「おお―――。データロード中のぐるぐるまで実装されていて、けっこう本格的ですね」
るので、そこだけ書き換えればどんな組織のメンバーも表示されるよ」
「ほんとだ! ウチの会社の人たちも表示されますね。雪菜さん、その中でもひときわ目を惹いてて
さすがです!」
「はいはい、そういうのは置いといてね。実際に開発する順番でファイルを見ていこうか。まずは
171
第11章 Reduxで非同期処理を扱う
import './Members.css';
return (
<>
<Helmet>
<title>{title}</title>
</Helmet>
<div className="Members" data-test="users">
<Header as="h2">{title}</Header>
{isLoading ? (
<Spinner />
):(
<Card.Group>
{users.map(user => (
<Card
key={user.id}
href={`https://github.com/${user.login}`}
target="_blank"
>
<Card.Content>
<Image floated="right" size="mini" src={user.avatar_url} />
<Card.Header data-test="card-header">
{user.login}
</Card.Header>
<Card.Meta>GitHub ID: {user.id}</Card.Meta>
</Card.Content>
</Card>
))}
172
11-3. Redux-Sagaを使いこなす
</Card.Group>
)}
</div>
</>
);
};
を直書きで渡してあげて画面がちゃんと表示されるか確認するの。CSS とか見た目の調整もこのとき
「なるほど、へ―――」
みて」
173
第11章 Reduxで非同期処理を扱う
とめてインポートできて扱いやすいからね。なお今回は、将来的に異なるドメインの複数のサービス
interface GetMembersParams {
companyName: string;
}
interface GetMembersResult {
users: User[];
}
174
11-3. Redux-Sagaを使いこなす
くするためにひとつのオブジェクトにまとめてて、実際にコールするときは getMembers.start()、
用した書き方なんだけど」
「なるほど」
src/actions/index.d.ts でサジェストしているインターフェースを意識しといてね。で、次はこれに対
175
第11章 Reduxで非同期処理を扱う
case ActionType.GET_MEMBERS_FAIL:
return {
...state,
isLoading: false,
error: action.payload.error,
};
default: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const _: never = action;
return state;
}
}
};
非同期通信処理を行っているところを見ておく必要があるね。src/services/github/api.ts を開いて」
interface ApiConfig {
baseURL: string;
timeout: number;
}
176
11-3. Redux-Sagaを使いこなす
try {
const response = await instance.get(`/orgs/${organizationName}/members`);
return members;
} catch (err) {
throw err;
}
};
return getMembers;
};
「……こ、これは。以前教わった『クロージャ 』ってやつじゃないでしょうか?」
*9
「そうそう、よくおぼえてたね。今回はほぼ使わないんだけど、外から共通設定を与えた状態で API
リクエストを実行するという汎用的な作りにしたかったの。従来ならクラスを使ってコンストラクタ
の引数でやるところだけど、せっかくなのでクロージャで書いてみました。
が使い勝手がよさげでよく使われてるのを見るので、そっちを採用してます」
とかですね」
列に格納、最終的にそれをリターンする関数を返してるの。実際の使い方はこんなふうになるかな」
*9 「3-5. クロージャ」
*10 https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch
*11 https://github.com/axios/axios
177
第11章 Reduxで非同期処理を扱う
try {
const users = await getMembers('facebook');
} catch(err) {
console.log(err);
}
「ふーん、なるほど。さすが雪菜さん、綺麗な設計ですね!」
てね。
try {
const api = getMembersFactory();
const users = yield call(api, companyName);
きも
「ここが今回の一番の肝だね。ジェネレータと Effect と呼ばれる Saga 特有の DSL っぽい API を使
った書き方。でもとりあえず、その説明は後回しにしよう。
178
11-3. Redux-Sagaを使いこなす
「ふむふむ、流れはわかりました」
くようになってるのは開発チームの信条 らしいんだけど、まあそういうもんだと思っておいて。あ
*12
・ call …… 外部の非同期処理関数をコールする。
クトを返す。
「なるほど、今回は F5 連打のリクエストに全て律儀に返す必要はないですからね」
*13 https://redux-saga.js.org/docs/api/
179
第11章 Reduxで非同期処理を扱う
取得できる。call で非同期処理関数を実行する場合、そのふたつめ以降の引数がその関数を実行する
際の引数になるの。
「ちょっと独特な書き方ですけど、読めないほどじゃないですね」
げの部分。src/index.tsx を見てみよう」
import './index.css';
import './styles/semantic.min.css';
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root') as HTMLElement,
);
180
11-3. Redux-Sagaを使いこなす
serviceWorker.unregister();
sagaMiddleWare.run(rootSaga);
ウェアを渡してあげたものを設定すると、Saga ミドルウェアが有効になる。もちろん複数のミドルウ
あと見逃しちゃいけないのが最後の行。sagaMiddleWare.run(rootSaga);。ここでさっき定義したル
「あ、そういうことですね。だんだん見えてきました」
interface StateProps {
users: User[];
isLoading?: boolean;
}
interface DispatchProps {
getMembersStart: (companyName: string) => void;
}
181
第11章 Reduxで非同期処理を扱う
useEffect(() => {
getMembersStart(companyName);
}, []);
return (
<Members companyName={companyName} users={users} isLoading={isLoading} />
);
};
行させてるわけ」
してると」
「うん、ちゃんと理解できてるね。よろしいよろしい。で、実際の開発のときに忘れるといけないの
182
11-3. Redux-Sagaを使いこなす
Component に書き換えること」
「私、よくこれを忘れがちで。全部適切に作ったはずなのに機能が動かなくて『何でかなー? 何で
かな−?」とおかしいと思われるところをずっと探し続ける羽目になるんだよね……」
「ふふふー、かわいい――」
「うーん、そうですねー。最初に散々おどされて覚悟してたんですが、やっぱり難しかったですねー。
でも雪菜さんの解説付きだったので、なんかわかったような気になっちゃってるのが自分でも怖いん
ですけど……」
「まあそれはそうだろうね。私だって最初は Saga、ちんぷんかんぷんだったし。検証と称して2日ほ
んど見当たらなかったので型解決に悩まされながら、悪戦苦闘してようやく簡単なコードが書けるよ
うになったくらいだから」
「えっ、雪菜さんでもそんなだったんですね。じゃ、いきなり私が使いこなすなんて無理じゃないで
すか」
「あなたの場合はこうやって私がお膳立てしたものがあるんだから、話が別でしょ。いいなー、私も
こんな優しい先輩に教えてもらいたかったなー」
「……いや自分で『優しい先輩』って言っちゃってますよ? いや私も思ってますけどね、
『優しい
先輩』って」
「うふふー、ありがとう」
183
第11章 Reduxで非同期処理を扱う
184
エピローグ
「今日が約束の一週間の最終日だったね。おめでとう、予定してたカリキュラムを終えることができ
ました。感想はどう?」
「あっという間の一週間でしたけど、まさか本当に一週間でここまで来られるとは思ってませんでし
た。雪菜さんのおかげです。ありがとうございます!」
「ふふふ、言ったでしょ? 私、教えるのうまいって」
「本当そうですよね。それに単なるコードの読み書きのコツだけじゃなく、React を支える思想や歴
史とか、どんな周辺技術がどういう経緯で出現してコミュニティに受け入れられてきたのかとか、ど
んなライブラリを選定するべきかみたいなお話が聞けたのもよかったです」
の View ライブラリでしかなくて、ちゃんとしたアプリを開発するためにはいくつもの周辺技術を組
み合わせる必要があるけど、最適解は日々移り変わっていてすぐ陳腐化してしまうから。それが React
の進化のダイナミズムを生む源泉になってるんだけど、初学者には優しくないよね」
プルに書けるし、かなり大規模で複雑なアプリでもぐちゃぐちゃにならずに書けそうで、すごく気に
入りました」
「えへへ。それで、来週からどうするんですか? もう私、実戦投入ですか?」
「そうだねー。でも実は、次のプロジェクト開始までに、まだ少し時間的に余裕があるんだよね」
「えっ、じゃあなんで最初にわざわざ1週間でって脅されたんですか私?」
「脅すなんて人聞きが悪い。まあ多少は危機感を持ってもらったほうが身が入るかなと思って、ちょ
っとはっぱをかけただけじゃない」
「ええ――、なんかだまされた気がします……」
「まあまあ。それで来週からだけど、もうちょっと現場の開発寄りに踏み込んだ内容のことをおぼえ
てもらおうかなって思ってるの。そのほうが実際の業務で開発に入ったときにスムーズだろうし。具
185
エピローグ
「そういう情報、あまり本とかに載ってないので、ぜひ教わりたいです!」
「うん、まあ楽しみにしといて。それより、ここまで一週間、本当におつかれさま。よくがんばった
ね。そのがんばりをねぎらってあげるから、いっしょに飲みに行かない? もちろん私のおごり」
な」
「……秋谷さん、ほんとぐいぐい来るよね。まあ特に隠すようなこともないので、聞きたいことは話
してあげるよ」
「この業界、女性エンジニアが少ないじゃないですか。活躍してる人となるとなおさら。だから雪菜
さんがどうやって今みたいになれたのかとか、すごく興味があるんですよね」
「わかったわかった。就業時間もちょうどさっき終わったところだし、話の続きは会社を出てからね」
「はい、じゃあ行きましょう!」
186
あとがき
たりにはストーリー小説が書けるくらい詳細なキャラクター設定があるのですが、基本的には芝崎さ
「秋谷さん頭よすぎ。初学者なのにそんなにすんなり理解できるもの?」という声があったのも事実。
それについては、逐一さかのぼりすぎて説明すると話の流れを分断してしまったり、ある程度の経験
者にとってはそれが煩わしく感じられるだろうと思い、意図的に物わかりのいいキャラにして話をは
しょってしまっている自覚はありました。
第2版で書き直すに当たり、そういった部分もできるだけフォローしたつもりですが、それでも同
様の感想を持たれる方がいらっしゃるかもしれません。Web アプリケーション開発の経験のある方
でしたら、最初から通して読めば理解するために最低限必要な要素を網羅されており、また都度、外
部資料へのリンクを紹介しているので、理解が難しいと感じられたらぜひ最初のほうから戻って読み
直してください。
初版ではその思想への理解の少なさと敷居の高さからか国内で今ひとつコミュニティの盛り上がり
目論見はある程度成功を収めた思います。そして第2版では、React のすぐれた最新の技術を現場に
スムーズに採り入れやすくするということも念頭に置いて書いてみました。特にリリースされたばか
ブラリとの共存について、まだまだ採用例が少ない中で参考になる情報を提供できたのではないかと
思っています。
なお著者として今回の改版の一押しポイントは、表紙に芝崎さんのイラストが掲載されたことです。
イラストレーターの黒木めぐみ様とのご縁があり、著者の要求を幾重にも上回るすばらしい出来の芝
崎さんを描いていただけました。芝崎さんの理知的で、確固たる自信を感じさせる微笑みをたたえた
姿。ああ、なんて麗しい……。
187
あとがき
イートしていただくと、著者が必ず目を通します。イラストについてのご感想もお待ちしています。
それではまた、次の機会に。
188
著者紹介
大岡由佳(おおおか・ゆか)
フリーランスで現在、React を専門にするフロントエンドエンジニア。過去には PHP や Rails のサー
バーサイドエンジニアや、人材系サービスのプロダクトマネージャーなどの経歴を持つ。直近でのお
気に入りの漫画は『かげきしょうじょ!!』と『ブルーピリオド』
。二年ほど毎日続けているストレッチ
@oukayuka。
黒木めぐみ(くろき・めぐみ) ◎表紙イラスト
漫画家、イラストレーター。
189
りあクト! TypeScript で始めるつらくない React 開発 第2版
2019 年 4 月 14 日 第 2 版第 1 刷発行
著者 大岡由佳
印刷・製本 日光企画
©くるみ割り書房 2019