業務のReactプロジェクトが昨年の秋(2020年)から関数コンポーネント+TypeScriptに移行しました。
元々はClassコンポーネントだったのですが、なぜ関数コンポーネントなのか?何が優れているのかを知っておきたかったのと、現場で学んだ書き方のTipsなどを記事にしようと思います。
関数コンポーネントとは?
Classに比べると簡潔に見通しよく書けるメリットがあったものの、Classの様にStateを持つ事ができなかった。(当たり前ですが、関数は状態を持つことができません。関数の実行が終われば、関数内の変数はスコープから外れ、アクセスできなくなります、クロージャーとかありますが、、)
そこで考案されたのがフックです。フックは、関数コンポーネントの中からReactの機能へ接続(Hooks into)することを実現しました。フックを用いることで、関数コンポーネントにおいてもクラスコンポーネントとほぼ同等の機能を実現することができる様になったわけです。
これによって、関数コンポーネントが主流になったらしいです。
では、フック(Hooks)とは?
フックは、関数コンポーネントに state やライフサイクルといった React の機能を “接続する (hook into)” ための関数です。フックは React をクラスなしに使うための機能ですので、クラス内では機能しません。今すぐに既存のコンポーネントを書き換えることはお勧めしませんが、新しく書くコンポーネントで使いたければフックを利用し始めることができます。
React公式ドキュメントから抜粋
ですので、私がフロント開発に携わるサービスもClassコンポーネントが残っていて混在してる状態です。
では、フックの実例を見ていきましょう。
useState
その名の通り、state(状態)を扱うためのフックです。
これを使うことで、コンポーネント内で状態を持つことができ、状態の変化で表示を変更できます。
状態と、それを更新するための関数を返します。更新されるとそのコンポーネントは再レンダリングが走り表示も変わります。
いかが例です。更新関数名は set + 状態名
が一般的です。
const [状態, 更新関数] = useState(初期状態);
const [name, setName] = useState("");
更新の方法は2種類です。
const [count, setCount] = useState<number>(0);
//引数に値を渡す
<button onClick={(e) => setCount(count - 1)}>マイナス</button>
//引数に関数を渡す
<button onClick={(e) => setCount((prevCount) => prevCount + 1)}>プラス</button>
useEffect
副作用の処理(DOMの変更、変数への代入、API通信、イベントリスナー)や、Classでいう、ComponentDidMount、ComponentDidUpdateを行う事ができる。
useEffect(() => {
実行したい処理
},[ 依存する値 ]);
第1引数にcallbackを入れて、第2引数に依存する値(監視する値)を配列で入れる
依存する値が変更される時だけ処理を実行させる使い方が一般的ですが、第2引数に入れる値によって以下の様に使い分けでができます。
第2引数に入れる値で以下の様に使い分けることができます。
useEffect(() => {
//何もいれない → レンダリングが走るたびに実行される、非推奨
})
useEffect(() => {
//空配列 → Componentがマウントされた最初の一回だけ実行(ComponentDidMountと同じ)
}, [])
const [name, setName] = useState("");
useEffect(() => {
//値を入れる → その値が変更があったときに処理が実行
}, [name])
※依存配列が複数ある場合、一つでも変わると再レンダリングが走ってしまうので、useEffectを依存する値ごとに分けるのが一般的です。
クリーンアップ
componentWillUnmount的な使い方もこのuseEffectでできます。
サンプルでは、Mount時にクリックを検出してincementNumを発火させるイベントリスナを登録し、「クリーンアップの関数」をreturnする事で、UnMount時に削除をするという事をしています。
const [currentNum, setCurrentNum] = useState(0);
const incrementNum = () => {
setCurrentNum(prev => prev + 1)
}
useEffect(() => {
window.addEventListener("mousedown", incrementNum)
return () => {
window.removeEventListener("mousedown", incrementNum)
}
}, [])
Part2に続く