読書メモ:Reactハンズオンラーニング
はじめに
最近のモダンアプリケーションのフロントエンドでよく使われているReactライブラリについて学ぶ必要があると思い購入しました。
Reactは、Next.jsやChakraなどのフレームワークやUIコンポーネントのベースとなっているライブラリです。
そのことから、モダンなWeb開発では、今や必須の知識なのではないでしょうか。
当書籍では、Reactのフロントエンドの開発部分だけでなく、前半部分のJavascriptの関数型プログラミングの技法の説明がわかりやすく大変役に立つものでした。
UniswapのUniversal Routerをデコードするプログラムを元のソースをらフォークし、関数型プログラミングで再開発したため、その際に得たコツと一緒にまとめてみたいと思います。
変数はすべてconst
キーワードで宣言する
純粋関数は、変数、配列、オブジェクトを書き換える副作用を伴いません。
そのため、すべてをconst
キーワードで定数にする必要があります。
これによって、変数が他のプログラムに書き換えられる副作用を無くすことができます。
アロー関数=>
を使った方が良い
アロー関数=>
を使った方が、記述をコンパクトにできます。
関数式において上記const
キーワードで、関数自体を定数として明示できるので、同じ関数名で上書きされることを防ぐことができます。
クラス宣言を使わない
純粋関数を定義していれば、オブジェクト指向開発で作らなくても目的を達成できます。
オブジェクト指向でプログラミングでgetter
メソッドやsetter
メソッドなどがあると副作用を伴うコーディングになってしまいます。
配列の値を変えるのに、For文でなくmap()
を使う
For文で配列を繰り返して探索して値を変えたくなりますが、関数型プログラミングでは、副作用を伴わないmap()
が使えます。
For文やIf文がネストした複雑で冗長かつ副作用を伴うプログラミングを防ぐことが出来ます。
配列の集計には、For文ではなくreduce()
を使う
平均値など、配列の内容を集計したいことがあります。Reduceで副作用を伴わないプログラミングが可能です。
これも同様に、命令型で行うとFor文やIf文を多数伴って副作用が発生することがあります。
配列の値を取り出したい場合、For文とIf文を使うのではなくfilter()
を使う
配列から特定の値を取り出すのに、For文で配列を検索し、特定の値をIf文で拾って、取り出すってことをやりますが、これは、filer()
で可能です。
副作用を伴う、For文で回しながら、副作用を伴うpop()
やsplice()
は使いません。
新たな配列やオブジェクト(辞書型)の生成に、map()
やreduce()
を使う
MapやReduceで新しいオブジェクトが作成可能です。
これも命令型に慣れていると、For文+push()
などで作りたくなりますが、map()
やreduce()
で効果的にプログラム可能です。
破壊的メソッドを使わない
繰り返しになりますが、新たな配列を加えるのにpush()
などは、元の配列を変更(破壊)してしまいます。concat()
などの元の配列のコピーを生成するメソッドを使ったり、新たにJavascriptで使えるようになったスプレッド構文を使います。
その他、pop()
、splice()
などの代わりにfilter()
を使わないといけません。
繰り返し処理は再帰を使う
繰り返し処理は、その関数のなかで、また同じ関数を呼び出す再帰を使います。
これにより、ForやWhileなどの命令型ループを使うわなくてもコーディングできます。
呼び出す関数が多すぎる場合は、高階関数を作ってreduce()
する
関数型プログラミングでは、関数連続して呼び出すのみですので、可読性が悪くなることがあります。
その場合は、高階関数に関数を渡してreduce()
で処理をします。
高階関数の例)
以下の高階関数に関数を渡すことで多数の関数を実行できます。
const compose = (...fns) => arg => fns.reduce((composed, f) => f(compsoed),arg);
テストが簡単でテスト駆動開発と相性が良い
関数同士で変数を共有しないため、完全に個別でテストをすることができます。
小さなテストに対して小さな関数を積み上げて開発することができTDDと相性が良いと思いました。
If文の代わりに、三項演算子が使える
副作用を伴わずにIf文を使うこともできますが、代わりにお馴染みの三項演算子(条件?真の場合の処理:偽の場合の処理)が使えます。
関数型プログラミングでは、条件分岐に副作用を伴わない三項演算子を良く使います。
辞書型オブジェクトのプロパティー(key: value)の追加はassign()
を使う
実は、本に載っていないためかなりハマりました。
こちらの記事が参考になります。
以下のようにイミュータブル(非破壊)で追加可能です。
const fullData = Object.assign({}, txnData, {'decodedData': decodedData});
実際に純粋関数で開発
学んだことを実践するために、UniswapのUniversal Routerのトランザクションデータをデコードするプログラムを純粋関数で実装しました。
対応するデコードを増やしつつフォーク元のプログラムをより圧縮することができました(純粋関数が優れているというわけではありません)。
関数型プログラミングを行うとどのようになるかの参考にしていただければ幸いです。
まとめ
今回は、「Reactハンズオンラーニング」の関数型プログラミングについて重点的にまとめました。
今後、フロントエンドのプログラムも開発予定ですが、関数型プログラミングは、バックエンドのプログラムでも非常に有効な手法だと思います。
思わぬ副作用によるプログラムの誤作動を防げます。
また、プログラムを思ったより短くすることができますし、GetterやSetterに落とし込みにくいようなオブジェクト思考独特な難しさも避けられます。
(実際にプログラムをオブジェクト指向で落とし込むのが難しいと思うのは自分だけでしょうか・・・)
欠点としては、命令型プログラムに慣れていると可読性が低くなることではないでしょうか?
また、多数の関数が連続的に呼び出されるということにも慣れないといけません。
今後は、フロントエンドにおいてもReactおよび関数プログラミングを積極的に取り入れ慣れていきたいと思います。