常用するReact hookの使い方について私個人の理解を書いてみます、至らぬところがあると思いますので、ご指摘いただけると幸いです。
0. React hookとは
名前が「use」から始まり、「react」からインポートされるものは基本的にreact hookです。
React hookはreactが提供している便利な道具のようなもので、その名前である「use(Something)」は大体「somethingとして使う」として訳せば理解しやすいと思います。
1. useState
基本構文
const [name, setName] = useState<string>("山田太郎");
[ 状態名, 状態を設定する関数 ] = useState<状態の型(tsのみ)>(初期値);
最も基本的なフックで、「状態として使う」です。
使い道はコンポーネントの状態を管理して、適切に再レンダリングさせることです。
上記コードを日本語に訳すと、「nameというstringタイプで初期値が山田太郎のフィールドをコンポーネントの状態として使う」です。もしどこかでsetName()が呼び出されたら、その変更に応じてコンポーネントが再レンダリングされます。
2. UseEffect
基本構文
const useEffect = (() => doSomething(), [trigger]);
useEffect = (行う処理, トリーガーとなるもの);
監視して、変化に反映するフックで、「(何らかの変化の)影響として使う」です。
トリーガーとなるものは配列形式で、空の配列だと初回レンダリング時に実行し、それ以外だとその中の変数の値が変化すると実行します。画面初回表示時にAPIを呼び出してデータを取得したり、スクロールバーの位置の変化で状態を変更したりする時によく使います。
上記コードを日本語に訳すと、「triggerを監視して、もし変化したら、その影響としてdoSomethingを呼び出す」です。
3. useContext
基本構文
// contextを作成
const NameContext = createContext();
// providerを作成
const NameProvider = ({ children }) => {
const [name, setName] = useState("山田太郎");
const updateName = (name: string) => {
setName(name);
};
return (
<NameContext.Provider value={{ name, updateName }}>
{children}
</NameContext.Provider>
);
};
const { name, updateName } = useContext(NameContext);
常用するフックの中でおそらく一番特殊なもので、コンポーネント内ではなく、コンポーネント間で状態を共有するために使われます。少し無理がある訳し方をすると、「文脈として共有する」でしょうか。
構文も特殊で、作成する構文と使用する構文に分けられています。
作成する側において、contextの作成とproviderの作成が必要です。contextの作成はシンプルで、作成するcontextの名前を決めればいいです。providerは作成したcontextの適用範囲を決めるコンポーネントで、providerで囲んだ、つまりそのchildren属性に該当するコンポーネントが作成されたcontextを使用できます。
カスタム性も常用フックの中で一番高いと思います。providerでcontext自体をuseStateで提供する他、contextを取得したり、更新したりする関数もproviderのchildrenに渡すことができます。
使用側においてはuseContextでproviderから渡された状態と関数を宣言し、使用できます。
Reactは基本的に親コンポーネントが子コンポーネントに親コンポーネントの値や関数などをpropとして渡し、親子の間でやり取りを行い、要するに隣り合わせの階層間でしか受け渡しができないのです。一階層ずつ受け渡すことも可能ですが、手間がかかるので、contextを作り、そのproviderで囲んだ全ての階層が直接に使用できるようにするのはuseContextの主な役割です。
4. useRef
基本構文
const nameRef = useRef();
// jsxまたはtsxの中
<input ref={nameRef} id="nameInput"/>
参照名 = useRef();
本当のDOMを操作する時に使うフックで、「(DOM要素を)参照として使う」です。
JavascriptがgetElementByIdやquerySelectorでDOM操作を行うのと違って、reactは基本的に仮想DOMを操作するので、jsxでinputタグをレンダリングしても、そのままでは入力しても何も出てこないわけです。そんなreactも直接にDOMを操作するにはuseRefを使い、実際の要素を参照として取得し、javascriptと似た形でfocus()などをさせることができます。
上記コードでは「idがnameInputの要素の参照としてnameRefを使用する」的な意味合いで、例えばnameRef.current.focus()を呼び出せば、実際のDOMにあるinput要素がフォーカスされます。
5. useReducer
基本構文
// reducer処理の定義
const reducer = (state, action) => {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
case "reset":
return { count: 0 };
default:
return state;
}
};
// useReducerでreducer処理を使用する
const [state, dispatch] = useReducer(reducer, { count: 0 });
要は高機能版のuseStateで、「(予め用意されたreducer関数を)reducerとして状態変更に使う」です。
useStateはset関数で同じタイプの値を状態に更新するシンプルな処理しか対応していませんが、useReducerは条件分岐など複雑な処理で状態を更新できます。
上記コードではactionのtypeによって初期値が0の状態countを更新します。例えばdispatch関数の引数を{type: "increment"}に設定すると、countが1足されることになります。
同じstateを更新する処理が多くなってきたらuseReducerに書き換えるといいかもしれません。
6. useMemo
基本構文
const nameKana = useMemo(() => kanjiToKana(name), [name]);
値 = useMemo(値を取得する処理, 値を取得するのに使用した別の値);
長い処理で算出される値をキャッシュして、算出元の値が変わらない限り同じ算出処理を何度もしないようにするフックで、「(値のキャッシュを)メモとして使用する」です。
上記コードだと、恐らくめんどくさい漢字をカナに変換する処理で取得したカナをキャッシュして、変換元の名前が変わらない限り、nameKanaを使用する場合、kanjiToKanaを実行せずにキャッシュされたnameKanaを使用できます。
7. useCallback
基本構文
const increment = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, [resetFlg]);
ほぼuseMemoの関数版のようなもので、「callback関数として使用する」です。
関数をuseCallbackの第一引数として書いて、あとは第二引数であるトリガーが変わらなければ、何回もその関数のオブジェクトを生成しないで済むので、処理が軽くなるといったイメージです。(関数オブジェクトを何回も生成すると他の副作用もあるようですが、実際遭ったことがなく、よくわかりません)
上記コードだと、resetFlgが親コンポーネントから渡されたpropだとすると、本来であればpropが改めて渡されると、MyComponentが再レンダリングして、() => {setCount(...)}も新しい関数オブジェクトとして作られますが、useCallbackを使うと、他のpropが変わっても、resetFlgが変わっていなければ、既存の関数オブジェクトを使い、新しく作成しません。
最後までご覧いただき誠にありがとうございます。Reactを独学してきて、何を作っても使いそうなのはこのぐらいですが、補足や指摘がございましたら是非お願いいたします。