純粋関数とは(React)
初めに
React のドキュメント読んでいたら、純数関数というのが出てきたので、調べてみました
純粋関数って何?
Document では
- それはそれ自身のビジネスを気にします。呼び出される前に存在していたオブジェクトや変数は変更されません。
- 同じ入力、同じ出力。同じ入力が与えられた場合、純粋な関数は常に同じ結果を返す必要があります。
https://beta.reactjs.org/learn/keeping-components-pure
ちょっと上の説明ではピンとこないので、数学の数式を思い出してみてください
y = 2x は
x = 2 だった場合いかなる場合も y = 4
x = 3 だった場合いかなる場合も y = 6
これを JavaScript で表すと
function double(number) {
return 2 * number;
}
になります。このdouble
は純粋関数です。引数に同じ値を渡してもいつも同じ値が返ってきます。
React での問題点
React はこのコンセプトに基づいて設計されています。React は、作成するすべてのコンポーネントが純粋な関数であると想定しています。これは、あなたが書いた React コンポーネントは、同じ入力に対して常に同じ JSX を返さなければならないことを意味します:
副作用を意図した結果
React はレンダリングプロセスは常に純数でなければいけません。
ダメな例のコンポーネントを書いてみます。
let guest = 0;
function Cup() {
// Bad: changing a preexisting variable!
guest = guest + 1;
return <h2>{guest}</h2>;
}
export default function TeaSet() {
return (
<>
<Cup />
<Cup />
// レンダリング回数によって結果が変わるので、結果が予測できない
</>
);
}
これは外部で宣言された変数を読み書きしています。コンポーネントを複数回呼び出すと異なる JSX が生成されることを意味します。
他のコンポーネントが Cup を呼ぶと、またさらに変わってきてどこで変わるかが予測できません。
React では Props としてguest
を渡せば何回呼び出しても同じ値になります。
返される JSX はguest
Props のみに依存するので、純粋なコンポーネントです。
function Cup({ guest }) {
return <h2>{guest}</h2>;
}
export default function TeaSet() {
return (
<>
<Cup guest={1} />
<Cup guest={2} />
<Cup guest={3} />
// 何回レンダリングしても同じ結果になる
</>
);
}
Strict Mode
開発中に2回コンポーネントが呼び出されていることがありますよね。
React は、開発中に各コンポーネントの関数を 2 回呼び出す「Strict モード」を提供します。
Strict モードは、コンポーネント関数を 2 回呼び出すことで、これらの規則に違反するコンポーネントを見つけるのに役立ちます。
なんで StrictMode を使うのかわからなかったですが、不純によるバグを見つけるためだったんですね。
とは言っても変数使いたい場合
コンポーネント内で作成したばかりの変数やオブジェクトをレンダリング中に変更しても問題ありません。
これはローカルミューテーションと呼ばれています。
function Cup({ guest }) {
return <h2>Tea cup for guest #{guest}</h2>;
}
export default function TeaGathering() {
let cups = [];
for (let i = 1; i <= 12; i++) {
cups.push(<Cup key={i} guest={i} />);
}
return cups;
}
最後に
- レンダリングが何回起きてもいいようにコンポーネントはレンダリング回数に依存しないような設計をします
- コンポーネントがレンダリングに使用する入力を変更しないでください。
コンポーネントの中の何かを変更したいときは、レンダリング中ではなく、イベントハンドラーを使ってレンダリング後にしましょう。
React では、通常、副作用はイベント ハンドラー内に属します。
イベント ハンドラーは、ボタンをクリックしたときなど、何らかのアクションを実行したときに React が実行する関数です。
コンポーネント内でイベント ハンドラーが定義されていても、レンダリング中には実行されません。したがって、イベント ハンドラーは純粋である必要はありません。
Discussion