フックとはReactのstateやライフサイクルを関数コンポーネントで利用するための機能です。
クラスコンポーネントでは使うことが出来ません。
目次
代表的なフック
ステートフック
状態を管理するためのフックです
クラスコンポーネントでのsetStateに当たります。
useStateを呼び出すことで、状態を管理する変数と関数が返ります。
下記の例ではcountが状態の入った変数で、setCountが状態を更新するための変数です。
useStateの引数で状態の初期値を設定することが出来ます。
この状態でcount変数を描画させると初期値の0が表示されます。
1 2 3 4 5 6 7 8 9 |
function Counter() { const [count, setCount] = useState(0); return ( <div> <h1>Count</h1> <p>{count}</p> </div> ); } |
状態を更新するにはsetState関数を使います
今回はuseStateで受け取ったsetCountがそれに該当します。
下の例ではクリック時にsetCountを呼び出してcountを+1するボタンを追加しました
1 |
<button onClick={()=> setCount(count+1)}>Count Up</button> |
またsetStateに関数を渡すことで前回の状態を利用することが出来ます
1 |
setCount(prevCount => prevCount+1) |
副作用フック
副作用フックとはcomponentDidMount、componentDidUpdateなど
クラスコンポーネントのライフサイクルを利用するためのフックです。
useEffectに関数を渡す形で利用します。
1 2 3 |
useEffect(()=>{ console.log('useEffect !') }); |
上記の例を先程のカウントコンポーネントに追加してみます
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function Counter() { const [count, setCount] = useState(0); useEffect(()=>{ console.log('useEffect !') }); return ( <div> <h1>Count</h1> <p>{count}</p> <button onClick={()=> setCount(prevCount => prevCount+1)}>Count Up</button> </div> ); } |
ブラウザをリロードするとコンソールにログが表示され、
CountUpボタンを押して状態を更新してみると、countの更新とともにコンソールのログが増えていくことが確認できます。
このようにcomponentDidMount、componentDidUpdateのタイミングでuseEffectが実行されていることがわかります
コンポーネントが更新されるたびにuseEffectが実行されることがわかりましたが、
useEffectの第二引数に配列を渡すことで実行を制御することが出来ます。
これを依存配列と呼びます。
まずは先程のuseEffectの第二引数に空の配列を渡してみます。
1 2 3 |
useEffect(()=>{ console.log('useEffect !') }, []); |
ブラウザをリロードするとコンソールにログが表示されますが、
count変数を更新してもログは表示されません。
このように空の配列を追加したことで依存する状態がないと判断され、
コンポーネントの更新ではuseEffectは実行されません。
次に、count変数をAとBに分け、useEffectの依存配列にcountAを追加します
1 2 3 4 5 6 |
const [countA, setCountA] = useState(0); const [countB, setCountB] = useState(0); useEffect(()=>{ console.log('useEffect !') }, [countA]); |
この場合、countAが更新されるとuseEffectが実行されますが、countBを更新してもuseEffectは実行されません。
このように、依存する状態変数を指定することでuseEffectの実行を制御することが出来ます。
コンテキストフック
コンテキストとはpropsを介さずにコンポーネント間でデータを渡す機能です。
コンポーネントフックとステートフックを利用することで、グローバルな状態管理を行うことが可能になります。
適当な子コンポーネントをカウンターコンポーネントに追加します。
1 2 3 4 5 6 7 |
function Component() { return( <div> <h2>Component</h2> </div> ) } |
1 2 3 4 5 6 7 8 |
function Counter() { return ( <div> <h1>Count</h1> <Component/> </div> ); } |
親コンポーネントでコンテキストを作成しexportしておきます。
これは子コンポーネントでimportするためです。
1 |
export const CountContext = React.createContext(); |
次にコンテキストのプロバイダコンポーネントで、子コンポーネントを囲みます。
親コンポーネントの最終的な形がこちらです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import React from 'react'; import Component from "./component"; export const CountContext = React.createContext(); function Counter() { return ( <div> <h1>Count</h1> <CountContext.Provider value={100}> <Component/> </CountContext.Provider> </div> ); } export default Counter; |
valueはコンテキストによって渡すデータです。
子コンポーネントでコンテキストのvalueを受け取るには
CountContextをimportしてuseContextの引数に渡すことでvalueを受け取ることが出来ます
子コンポーネントの最終的な形になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import React, {useContext} from 'react'; import {CountContext} from "./Counter"; function Component() { const count = useContext(CountContext); return( <div> <h2>Component</h2> <p>count: {count}</p> </div> ) } export default Component; |
コンテキストによって渡されたデータが表示されていることが確認できます。
コンテキストでステートフックを渡すとどうなるでしょうか
親コンポーネントでステートフックを定義し、valueに渡します。
今回count変数の表示は親で行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
function Counter() { const [count, setCount] = useState(0); return ( <div> <h1>Count</h1> <p>count: {count}</p> <CountContext.Provider value={[count, setCount]}> <Component/> </CountContext.Provider> </div> ); } export default Counter; |
子コンポーネントでcountのステートフックを受け取り、
クリック時に+1するボタンを追加します。
1 2 3 4 5 6 7 8 9 |
function Component() { const [count, setCount] = useContext(CountContext); return( <div> <h2>Component</h2> <button onClick={()=> setCount(count+1)}>Count Up</button> </div> ) } |
このカウントアップ処理が親のcount変数に反映されていることが確認できます。
カスタムフック
フックを利用するにはコンポーネントである必要はなく、
別の関数にフックを抽出することでコンポーネントとロジックを分離し、
再利用可能な独自のフックを作成することができます。
今回のカウントコンポーネントからuseStateフックを抽出してみます。
カスタムフックの作成はコンポーネントに作成したフックをそのままコピーするだけで、
何も特別なことは必要ありません。
今回はcountをカウントアップ、カウントダウンする関数も追加しました。
最後にコンポーネントで使用する変数や関数をreturnしておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import {useState} from "react"; function useCount() { const [count, setCount] = useState(0); function countUp() { setCount(prevState => prevState+1); } function countDown() { setCount(prevState => prevState-1); } return { count, countUp, countDown } } export default useCount; |
カウントコンポーネントではimportしたuseCountを実行して
count変数とcountUp, countDown関数を受け取ります。
あとはuseStateの時と同じように使うだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import useCount from './useCount' function Counter() { const {count, countUp, countDown} = useCount(); return ( <div> <h1>Count</h1> <p>{count}</p> <button onClick={countUp}>Count Up</button> <button onClick={countDown}>Count Down</button> </div> ); } |
カスタムフック内のuseStateが機能していることがわかります
フックに対応したライブラリ
主要なReactライブラリにはフックを提供しているものがあります。
ReactRouter
例えばReactRouterにはhistoryを返すuseHistory、
URLのクエリからパラメータを取得して返すuseParamsなど
1 2 3 |
let history = useHistory(); let { id } = useParams(); history.push('/home'); |
Redux
Reduxではdispatch関数を返すuseDispatch、storeからstateを取得するためのuseSelectorなど
Reduxのフックを利用することでconnectなどの煩雑な処理をする必要がなくなり(多少は)簡潔な利用が可能になります
1 2 3 4 5 6 |
import { useDispatch, useSelector } from "react-redux"; import { countUpAction } from "./actions"; const dispatch = useDispatch(); const count = useSelector(state => state.count); dispatch(countUpAction()); |
またReduxフックをカスタムフックとしてコンポーネントから抽出することで、
グローバルで再利用可能な状態管理を実現することができます。