React設計:useEffectとコンポーネント分割の導入スライド
React設計:useEffectとコンポーネント分割 - 副作用とは
副作用にあたる仕事
画面を作る以外の仕事は、すべて副作用です。何がどこに触れているか見てください。
どれも React が管理する画面の外に触っています。
| 仕事 | 触れる場所 |
|---|---|
| document.title を書きかえる | ブラウザのタブ |
| タイマーを動かす | ブラウザの時計 |
| サーバーにデータを取りに行く | ネットワークの先 |
| ブラウザに値を保存する | ブラウザの保存領域 |
1 / 5
描画の中では書かない
描画の本体は画面を作る式です。押されたときの仕事は、押されたときに動く場所へ移します。
描画の本体に書く
document.title = "いいね " + likes;
function handleLike() {
setLikes(likes + 1);
}ハンドラの中に書く
function handleLike() {
const next = likes + 1;
setLikes(next);
document.title = "いいね " + next;
}押したときだけ動けばよい仕事は、押したときに動く場所に置きます。
2 / 5
React設計:useEffectとコンポーネント分割 - 副作用とは
順番を目で確かめる
1手ずつ進めて、画面とタブのどちらが先に変わるか確かめてください。
- 正しい版は画面が先に変わり、タブはそのあとで変わる
- 描画の中で書く側に切り替えると、タブのほうが先に変わってしまう
- 描画の途中で外の世界を触ると、起きる順番が読めなくなる
3 / 5
副作用のつまずき
副作用の置き場所で、つまずきやすい書き方です。
描画の途中で document.title を書きかえる
呼ばれる回数も順番も読めなくなる
タイマーや通信を部品の本体に直接書く
描画のたびに新しく作られ、増え続ける
押したときだけでよい仕事を外に出しっぱなしにする
表示される前から外の世界を触ってしまう
外に触るなら、きっかけが起きたときだけにします。
4 / 5
考えてみよう外しても進めます
setLikes(likes + 1) は副作用ですか。
選ぶと、この枚の中で答えと理由を確かめられます。
5 / 5
React設計:useEffectとコンポーネント分割 - useEffect
書き方の骨組み
useEffect は2つ引数を取ります。処理の関数と、いつ動かすかを決める配列です。
2つめの空の配列を忘れると、動くタイミングが変わります。
useEffect(() => {
// 描画のあとにしたい処理
}, []);1 / 5
読み込み完了までの流れ
最初に読み込み中が出て、そのあと effect が走ると読み込み完了に変わります。
あおいのプロフィール
読み込み完了
setStatuseffectが変える"読み込み中"最初の表示2 / 5
importを忘れると
useEffect を使うときは、import の行にも追加します。
useStateだけ書く
import { useState } from "react";useEffectも一緒に書く
import { useState, useEffect } from "react";書き忘れると、useEffect が未定義のままエラーになります。
3 / 5
React設計:useEffectとコンポーネント分割 - useEffect
空の配列の有無
再描画した回数を動かして、上と下で実行回数がどう違うか比べてください。
- 再描画の回数を増やしても、上の実行札は1のまま増えない
- 下の実行札は、再描画のたびに1枚ずつ増えていく
- 空の配列を書き忘れると、effect は毎回の描画で動いてしまう
4 / 5
考えてみよう外しても進めます
useEffect の2つめに空の配列を渡したとき、処理は何回動きますか。
選ぶと、この枚の中で答えと理由を確かめられます。
5 / 5
React設計:useEffectとコンポーネント分割 - 依存配列
3通りの依存配列
依存配列に何を書くかで、effect が走るタイミングが変わります。
配列の中身が多いほど、走る条件も増えます。
| 書き方 | 走るタイミング |
|---|---|
| 書かない | 描き直すたび毎回走る |
| 空の配列 | 最初に1回だけ走る |
| [likes] | 最初と、likes が変わったときに走る |
1 / 5
React設計:useEffectとコンポーネント分割 - 依存配列
countを動かして比べる
count を動かし、依存配列から外したときの違いを確かめてください。
- count を動かすと、前回と今回の依存配列の中身が変わる
- 中身が変われば実行、同じなら実行しないと札の色で分かる
- 依存配列から外すと、タブの文字は最初の値のまま固まる
2 / 5
依存配列のつまずき
書き方を選ぶときによくある3つの間違いです。
2つめの引数を書かない
描き直すたびに走り、更新がまた描き直しを呼んで止まらない
使っている値を依存配列に入れ忘れる
値は変わっているのに、effect は古い値のまま動く
1回だけにしたいからと機械的に空にする
中で読んでいる値が更新されても反映されない
基準は1つ、effect の中で使っている値を全部入れることです。
3 / 5
何を入れるかの基準
空にすればいいと決め打ちすると、中で使う値を見落とします。
機械的に空にする
useEffect(() => {
setRunCount(likes);
}, []);使っている値を入れる
useEffect(() => {
setRunCount(likes);
}, [likes]);effect の中で読んでいる値は、依存配列にも書きます。
4 / 5
考えてみよう外しても進めます
effect の中で likes を読んでいるのに、依存配列を空にするとどうなりますか。
選ぶと、この枚の中で答えと理由を確かめられます。
5 / 5
描画のあとに合わせる
ボタンを押してからタブの文字が変わるまで、この順で進みます。
stateが変わる
いいねボタンを押し、likes が更新される
描画が終わる
Reactが新しい画面をブラウザに反映する
effectが走る
document.title に likes を使った文字を入れる
effect は描画のあとに動くので、タブは1周遅れて追いかけます。
1 / 5
タブに文字を出す
document.title に文字を入れると、タブの文字が変わります。右のタブ名がその結果です。
document.titleタブの文字を書きかえる2 / 5
合わせ忘れの落とし穴
いいね数とタイトルがずれるとき、よくある原因です。
依存配列を空にする
タイトルは最初の0件のまま止まる
onClick の中で document.title を書く
取り消しボタンなど経路が増えるたびに書き足す羽目になる
画面用の文字の更新を忘れる
タブは変わるのに画面の文字だけ古いまま残る
きっかけの操作ではなく、合わせたい結果を effect に書きます。
3 / 5
書く場所の選び方
きっかけの操作を追うか、合わせたい結果を書くかで手数が変わります。
onClickに書く
<button
onClick={() => {
setLikes(likes + 1);
document.title = likes + 1 + "件";
}}
>effectに書く
useEffect(() => {
document.title = "いいね " + likes + " 件";
}, [likes]);経路が増えても、直すのは effect の1か所だけで済みます。
4 / 5
考えてみよう外しても進めます
いいねを取り消すボタンも作ったとき、onClick にタイトル更新を書く方式だとどうなりますか。
選ぶと、この枚の中で答えと理由を確かめられます。
5 / 5
更新の書き方で変わる
タイマーの中で state をどう更新するかで、増えつづけるか1で止まるかが変わります。
そのままの値を足す
useEffect(() => {
setInterval(() => {
setSeconds(seconds + 1);
}, 1000);
}, []);更新関数で受け取る
useEffect(() => {
setInterval(() => {
setSeconds((s) => s + 1);
}, 1000);
}, []);1 / 5
React設計:useEffectとコンポーネント分割 - 時計とクリーンアップ
片付けを返すeffect
この画面を開いた回数を増やして、動くタイマーの本数を確かめてください。
- 開いた回数を増やすと、タイマーの行が下に積み上がる
- 片付けを返すようにすると、生きているのは常に最後の1本になる
- 積み上がったタイマーは、消したはずの画面のぶんも動き続けている
2 / 5
つまずきどころ
後始末の関数を返すときに、2つのミスがよく起きます。
片付けの関数を呼び出した結果を返す
その場で1度呼ばれるだけで、後始末には使われない
そのままの値に1を足す
effect を作った時点の値に固定され、1で止まる
返すのは、呼び出した結果ではなく関数そのものです。
3 / 5
React設計:useEffectとコンポーネント分割 - 時計とクリーンアップ
後始末を書く形
id を変数で受け取り、return で片付ける関数を渡します。
この5行が、後始末つき effect の基本の形です。
useEffect(() => {
const id = setInterval(() => {
setSeconds((s) => s + 1);
}, 1000);
return () => clearInterval(id);
}, []);4 / 5
考えてみよう外しても進めます
後始末の関数が呼ばれるのはどんなときですか。
選ぶと、この枚の中で答えと理由を確かめられます。
5 / 5
全部書くか、分けるか
全部を App に直接書くか、意味のまとまりごとに部品を呼ぶかで、見た目は同じでも構造が変わります。
べた書きする
export default function App() {
return (
<div>
{/* 見出しも自己紹介も作品も
全部ここに書く */}
</div>
);
}まとまりで呼ぶ
export default function App() {
return (
<div>
<Header />
<ProfileCard />
<Footer />
</div>
);
}1 / 5
React設計:useEffectとコンポーネント分割 - 部品ツリーで考える
親を外すと子も消える
Card の枚数を動かし、List を外してみて、木と画面が連動する様子を確かめてください。
- 枚数を動かすと、木の Card と画面の作品が同時に増減する
- List を外すと、ぶら下がる Card もまとめて消える
- 画面に並ぶ作品の数は、木の Card の数とかならず一致する
2 / 5
React設計:useEffectとコンポーネント分割 - 部品ツリーで考える
切れ目の3つの手がかり
囲む場所に迷ったら、次の3つを手がかりにします。
3つとも、もう一段細かくできないかを自分に聞くための質問です。
| 手がかり | 見分け方 |
|---|---|
| 見た目の区切り | 見出しや枠線ではっきり分かれている |
| 繰り返し | 同じ形が2回以上出てくる |
| 説明のしやすさ | 一言で何を出す箱かを答えられる |
3 / 5
一言で言えないなら
説明が2つの話になったら、その箱はまだ大きすぎます。
一言で答えられない箱
自己紹介と作品一覧を出す箱
分けると一言で言える
自己紹介の箱 と 作品一覧の箱
説明に「と」が入ったら、そこは2つに分かれます。
4 / 5
考えてみよう外しても進めます
部品ツリーで List を Card ごと外すと、画面のカードはどうなりますか。
選ぶと、この枚の中で答えと理由を確かめられます。
5 / 5
子で持つか、親で持つか
兄弟それぞれが自分の state を持つと、値がずれます。共通の親へ移します。
子がそれぞれ持つ
function FilterButtons() {
const [category, setCategory] =
useState("all");
// ここだけの値になる
}共通の親が持つ
function App() {
const [category, setCategory] =
useState("all");
// 両方の子へ props で配る
}同じ意味の値を2か所に置かないのが鉄則です。
1 / 5
React設計:useEffectとコンポーネント分割 - stateを持ち上げる
1手ずつ持ち上げる
1手ずつ進めて、同じ値がどこにそろうか確かめてください。
- はじめは2つの部品の値がずれたまま
- state が親へ移り、そのあと両方の子へ props が届く
- 最後は押したときに、親を含めた3か所が同時に変わる
2 / 5
消し忘れるとずれる
持ち上げるときによくある2つのミスです。
持ち上げたのに子の useState を消し忘れる
押すと子の中の値だけ変わり、親から来る値と食い違う
1部品でしか使わない値まで親へ上げる
親が state を抱えすぎて読みにくくなる
上げるのは、2つ以上の部品が見る値だけです。
3 / 5
React設計:useEffectとコンポーネント分割 - stateを持ち上げる
配る先で渡すものが違う
同じ値でも、配る先によって渡すものが変わります。
更新関数もそのまま props として渡せます。
| 配る先 | 渡すもの |
|---|---|
| ボタンの部品 | いまの値と、変えるための関数 |
| 一覧の部品 | いまの値だけ |
| 更新関数 | ただの関数なので props として渡せる |
4 / 5
考えてみよう外しても進めます
持ち上げたあとも子の useState を消し忘れると、何が起きますか。
選ぶと、この枚の中で答えと理由を確かめられます。
5 / 5
名前を変えずに渡す
受け渡しでいちばん多いミスは、途中で名前を書きかえてしまうことです。
名前を変えて渡す
<WorkList author={owner} />同じ名前で渡す
<WorkList owner={owner} />受け取る側は owner という名前で取り出すので、渡す名前もそろえます。
1 / 5
React設計:useEffectとコンポーネント分割 - propsのバケツリレー
どこまで届いたか
どこまで渡したかを動かして、値が下りていく道を追ってください。
- いちばん下まで進めると、Card まで値が届く
- 途中の List が渡し忘れると、下は undefined になって崩れる
- 色が変わっているところが、実際に渡っている範囲
2 / 5
深くなると起きること
2階層くらいなら分かりやすい仕組みですが、深くなるとこうなります。
渡す値を1つ増やす
途中の部品を全部書きかえることになる
途中の1か所で渡し忘れる
いちばん下だけ静かに空になる。エラーは出ない
使わない値を props で持ち歩く
途中の部品の props が関係ない項目でふくらむ
2つめが特にやっかいです。上から順に、どこで落としたかを探すことになります。
3 / 5
3階層を渡した結果
いちばん下まで届いた値を使って、名前と作品名を並べます。書いたコードと画面の対応を確認してください。
- あおい / レシピ検索アプリ
- あおい / 買い物メモツール
- あおい / 写真ギャラリー
{owner} / {title}3階層渡ってきた値4 / 5
考えてみよう外しても進めます
途中の部品で、渡す名前だけを author に変えて渡すとどうなりますか。
選ぶと、この枚の中で答えと理由を確かめられます。
5 / 5
まとめるか、分けるか
1つの部品が複数の役目を抱えると、こうなりがちです。
役目をまとめる
function App() {
// プロフィールも作品一覧も
// ここに全部書く
}役目ごとに分ける
function App() {
return (
<div>
<ProfileCard />
<WorkList />
</div>
);
}App を読むだけで、ページが何と何でできているか分かるようになります。
1 / 5
React設計:useEffectとコンポーネント分割 - 1部品1責務
分けたくなる合図
役目が増えていく部品には、だいたい先に合図が出ます。
逆にこれらが出ていないなら、行数が多くても分けなくて構いません。
| 合図 | 何が起きているか |
|---|---|
| 説明が2つの話でつながる | 役目が2つ以上入っている |
| 上半分と下半分で別のデータを触る | 触っているデータが違う |
| 片方だけを別の画面でも使いたい | 独立した部品にできる |
| 直したい場所にスクロールが要る | 分けどきのサイン |
2 / 5
分け方がずれる合図
分けたつもりでも、基準を間違えると起きる困りごとです。
見た目が近いという理由で同じ部品にする
触っているデータが違うのに1つにまとまる
親から渡す props が10個になる
分け方が役目の境目からずれている
行数が多いというだけで分ける
役目が1つならそのままのほうが読みやすいこともある
props の数は、分け方が合っているかを測る物差しになります。
3 / 5
React設計:useEffectとコンポーネント分割 - 1部品1責務
役目を1行で言う
自分が書いた部品の役目を、コメント1行で言えるか試してみてください。
1行で言えないなら、まだ役目が複数残っています。言えるまで分けます。
// ProfileCard プロフィールを表示する
// WorkList 作品の一覧を表示する4 / 5
考えてみよう外しても進めます
1つの部品が、関係のない2つのデータを表示しています。分け方の目安はどれですか。
選ぶと、この枚の中で答えと理由を確かめられます。
5 / 5
React設計:useEffectとコンポーネント分割 - フォルダ構成
直す理由で分ける
同時に直すものを近くに、別々に直すものを遠くに置くと、開くファイルに迷いません。
作品を足すだけなら、components/ を開く必要はありません。
| 直したいこと | 開く場所 |
|---|---|
| 作品を1件足したい | data/works.js |
| 見た目を直したい | components/ の中 |
| ページの骨組みを直したい | App.jsx |
1 / 5
1つにまとめるか
部品もデータも一緒くたにすると、直す理由が違うものが同じ場所に並びます。
componentsに全部入れる
components/
Header.jsx
WorkCard.jsx
works.jscomponentsとdataに分ける
components/
Header.jsx
WorkCard.jsx
data/
works.js作品を1件足したいだけなのに、部品のファイルまで並んで探す手間が増えます。
2 / 5
1つ上がって1つ降りる
components の中のファイルから、data の中のファイルまでの道のりです。
いまいる場所
components の中のファイルにいる
1つ上がる
プロジェクトの直下へ戻る
1つ降りる
data の中へ入る
たどり着く
../data/works.js に着く
上がる数を勘で足さず、自分がいまどこにいるかを先に確かめます。
3 / 5
パスでよくある事故
書き方をひとつ間違えるだけで、見つからないというエラーになります。
上がる数を勘で1つ減らして書く
見つかりませんというエラーになる
フォルダを何段も深く掘る
パスを書くだけで疲れる
入門の規模なら、components と data の2つで十分です。
4 / 5
考えてみよう外しても進めます
components の中のファイルから data/works.js を読み込みます。正しい書き方はどれですか。
選ぶと、この枚の中で答えと理由を確かめられます。
5 / 5
React設計:useEffectとコンポーネント分割 - 命令的vs宣言的
手順か、あるべき姿か
変える項目を増やして、上と下で行数がどう変わるか見くらべてください。
- 手順を書く側は、項目が増えるほど行数も増える
- 手順を1つ書き忘れると、そこだけ古いまま残る
- あるべき姿を書く側は、書き忘れようがない
1 / 5
React設計:useEffectとコンポーネント分割 - 命令的vs宣言的
3つの場面で見くらべる
このコースで書いたコードを、命令的だったらどうなるかで並べます。
右側は、この65回で一度も書かなかった書き方です。
| Reactではこう書いた | 命令的に書くなら |
|---|---|
| state を変える | 画面の要素を取得して書きかえる |
| onClick で更新関数を呼ぶ | クリックのたびに直接いじる |
| map で一覧を作る | 1件ずつ要素を足していく |
| 三項演算子や論理積で出し分ける | if 文で要素を足したり消したりする |
2 / 5
つまずきどころ
宣言的に書いているつもりで、命令的な癖が出る瞬間があります。
部品の中で画面の要素を直接書きかえる
次の描き直しで消えてしまう
画面のどこを直すかを手順で考えはじめる
どの state を変えるかに戻れなくなる
迷ったら、まずどの state を変えればその画面になるかを考えます。
3 / 5
考えてみよう外しても進めます
React の部品の中で、画面の見た目を直したいとき、正しいのはどれですか。
選ぶと、この枚の中で答えと理由を確かめられます。
4 / 5
命令的は差分の当て方を書き、宣言的はあるべき姿を書く。
このコースで書いたコードは、全部あるべき姿を書く側でした。
5 / 5
React設計:useEffectとコンポーネント分割 - 完成と次のステップ
身についた6つの力
このコースでできるようになったことを、まとめて振り返ります。
| 身につけた力 | このコースでやったこと |
|---|---|
| JSX | 見た目を書いた |
| props | 親から子へ値を渡した |
| useState | 変わる値を持った |
| イベント | state を変えた |
| map と条件分岐 | データから画面を作った |
| useEffect | 外の世界とつないだ |
1 / 5
次に進む3つの道
本を読む順番のように、次はこの順で進みます。
TypeScript入門
受け渡しのミスを書いている最中に教えてくれる
React実践
props のバケツリレーを別の仕組みで解消する
Next.js入門
ページ切り替えとサーバー処理まで通して公開する
TypeScript が前提になる場面が多いので、この順がいちばん詰まりにくいです。
2 / 5
React設計:useEffectとコンポーネント分割 - 完成と次のステップ
迷ったらstateを探す
分からなくなったら、まずこの1行に戻ります。
画面を変えたくなったら、状態から考えます。
// どの state を変えれば、この画面になるか3 / 5
考えてみよう外しても進めます
TypeScript入門を、React実践より先に受けるのはなぜですか。
選ぶと、この枚の中で答えと理由を確かめられます。
4 / 5
教材の題材ではなく自分の題材にした瞬間に、覚え方が変わる。
次はこのサイトを、自分のものに書きかえるところからです。
5 / 5
