Reactの状態管理シリーズの続きです。以前はステート、コンテクスト、Reduxなどいろいろやって来ましたが、フック(React Hooks)はまだやっていませんでした。コンテクストはクラスコンポーネントでしか使えませんでしたがフックは関数コンポーネントでも使えます。ただ、完全な代替手段ではなくコンポーネント間のデータの受け渡しはコンテクストと併用する必要がありそうです。基本のフックと言われるuseState、useEffect、useContextを触ってみます。それではいきましょう!
【目次】
※参考:前回記事
【React】コンテクスト(Context)でネストされたコンポーネントに値を渡す - クモのようにコツコツと
※参考:Reactを習得するためにやったことまとめ
qiita.com
前回までのおさらい
React状態管理シリーズ
ステート
※参考:【React】Reactプロジェクトでステートを事始め(setState()、タイマー処理、イベント処理) - クモのようにコツコツと
※参考:【React】ステートで制御構造(条件分岐、ループ) - クモのようにコツコツと
コンテクスト
※参考:【React】コンテクスト(Context)でネストされたコンポーネントに値を渡す - クモのようにコツコツと
Redux
※参考:【React】Reduxを事始める(インストール〜ストア、レデューサー、プロバイダー設定) - クモのようにコツコツと
コンテクストはクラスコンポーネントでしか使えなかったがフック(React Hooks)は関数コンポーネントでも使えるようだ。さっそくトライ!
環境設定
まずはcreate react appをインストール。アプリ名は「react-hook-test」
$ npx create-react-app react-hook-test
フォルダに移動
$ cd react-hook-test
アプリを起動
$ npm start
よし、クルクルしとる。
いったん「Controle + C」で閉じる。
gitの初期設定(「.gitフォルダ」が作られる)
$ git init
GItHubリポジトリと紐付け
$ git remote add origin https://github.com/ryo-i/react-hook-test.git
フック(React Hooks)とは何か
で、このフックとは何なのか。
「フック」自体はプログラミングで一般的に使われる言葉
プログラムの中に独自の処理を割りこませるために用意されている仕組み。
※参考:https://wa3.i-3-i.info/word12296.html
そういえばWordPressでもフック機能があった。
※参考:フックって?WordPressのカスタマイズ方法を説明します! | 株式会社LIG
Reactの公式ドキュメントによれば
フック (hook) は React 16.8 で追加された新機能です。state などの React の機能を、クラスを書かずに使えるようになります。
※参考:フックの導入 – React
フックとは、関数コンポーネントに state やライフサイクルといった React の機能を “接続する (hook into)” ための関数です。フックは React をクラスなしに使うための機能ですので、クラス内では機能しません。
※参考:フック早わかり – React
クラスなしに関数コンポーネントで使える、というのがポイントのようだ。(このページの「早わかり」って翻訳、いいなw)
フックを使うにあたり、ドキュメントのフックページをかなり読み込んでみたのだが、いまいち概念が理解できなかった。
「基本のフック」といわれる3つの機能をやってみる。
- useState:ステートを設定
- useEffect:副作用の設定
- useContext:コンテクストと連携
useStateでステートを設定
まずは一つ目の「useState」、ステートの設定ができる。
ステートフルな値と、それを更新するための関数を返します。
「Count.jsx」というファイルを作成。
React
とuseState
をインポート
import React, { useState } from 'react';
Count
コンポーネントを作成
function Count() { const data = { name: '羊', count: 0 } const [count, setCount] = useState(data.count); const name = data.name; return ( <div className="reactHookTest"> <h2>React Hooks事始め</h2> <p>{name}が {count} 匹</p> <button onClick={() => setCount(count + 1)}> 数える </button> </div> ); }
- 変数
data
でステートの初期値を設定、連想配列name
キーの値は「羊」、count
キーの値は「0」 - 変数
count
、setCount
にuseState()
でステート設定(引数はdata.count
キー) - 変数
name
でdata.name
を取得 - JSX設定、pタグで
name
とcount
を呼び出す
buttonタグをクリックすると(onClick
)setCount
でcount
を1増やす
[count, setCount]
の箇所はJSの分割代入。変数名側も配列にして一気に設定できる。
最後にCount
コンポーネントをエクスポート
export default Count;
App
コンポーネントにCount
コンポーネントを配置する
「App.js」の冒頭でCount
コンポーネントをインポート
import Count from './Count';
App
コンポーネント内のaタグの下にCount
コンポーネントを配置
function App() { return ( <div className="App"> <header className="App-header"> <!-- 中略 --> <a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer" > Learn React </a> <Count /> </header> </div> ); }
ブラウザで挙動を確認。アプリを起動する
$ npm start
お、Counter
コンポーネントが表示されている!
「羊」は
name
で「0匹」の0はcount
部分
「数える」ボタンを押すと羊の数が増える!
ステートで状態が変わっているということだ。
ページをリロードするとステートの初期値「0匹」に戻る
useEffectで副作用の設定
次はuseEffect
、副作用の設定ができる。
そもそも副作用とは何か
プログラミングにおいて、式の評価による作用には、主たる作用とそれ以外の副作用(side effect)とがある[1][2]。 式は、評価値を得ること(※関数では「引数を受け取り値を返す」と表現する)が主たる作用とされ、それ以外のコンピュータの論理的状態(ローカル環境以外の状態変数の値)を変化させる作用を副作用という
うむ、よくわかんない、、
「副作用」とは、関数またはメソッドを実行した時に、オブジェクトの属性が変化することを指しています。
※参考:プログラミングで副作用と状態ってなに? | 民主主義に乾杯
なるほど!こちらの方がわかりやすい。
そしてReactでの副作用とは
データの取得、購読 (subscription) の設定、あるいは React コンポーネント内の DOM の手動での変更、といったものはすべて副作用の例です。
useEffect
さっそく使ってみる。先ほどのCounter
コンポーネントを改造する。
冒頭でuseState
に加えuseEffect
もインポート
import React, { useState, useEffect } from 'react';
Count
コンポーネント
function Count() { const data = { name: '羊', count: 0 } const [count, setCount] = useState(data.count); const name = data.name; useEffect(() => { document.title = `React Hooks事始め - ${name}が ${count} 匹`; }); return ( <div className="reactHookTest"> <h2>React Hooks事始め</h2> <p>{name}が {count} 匹</p> <button onClick={() => setCount(count + 1)}> 数える </button> </div> ); } export default Count;
useEffect()
を追加、ページのtitleタグにもname
やcount
を呼び出す。
${name}
、${count}
などはstyled-componentsの時にも使ったJSのプレースホルダの記法。
※参考:【React】styled-componentsのcreateGlobalStyleでbodyタグにCSS設定(Reactとメタ言語の比較-4) - クモのようにコツコツと
このように副作用の処理を追加することができる。
ブラウザの動作を確認
ページをリロードするとtitleに「羊が0匹」が追加された!
カウントを増やすとtitleも動的に変わる!
ページをリロードするとtitleもステート初期値の「0匹」に戻る
useContextでコンテクストと連携
最後はuseContext
。コンテクストと連携できる。
これまでステートをコンポーネントの中に設定していたが、コンテクストはコンポーネントの外側からステートの値を受け取ることができた。
※参考:【React】コンテクスト(Context)でネストされたコンポーネントに値を渡す - クモのようにコツコツと
しかしフックは関数の中で使うルールのようだ
ネストした関数の中でフックを呼び出さないでください。
※参考:フック早わかり – React
コンポーネントの状態を参照している場合、フックはコンポーネント間で共有するのに役立ちません。コンポーネントの状態は、コンポーネントに対してローカルです。
※参考:javascript — ReactのuseState()フックを使用してコンポーネント間で状態を共有することは可能ですか?
コンポーネントを超えるステートはコンテクストで設定してuseContext
で受け取る
コンテクストオブジェクト(React.createContext からの戻り値)を受け取り、そのコンテクストの現在値を返します。
ドキュメントの事例では同一ファイル内で処理が完結しているが、今回、別ファイル間でデータをやりとりする形にしてみた。
まず親コンポーネント「App.js」でコンテクストを設定する。コンテクスト設定は下記と同じ手順。
※参考:【React】コンテクスト(Context)でネストされたコンポーネントに値を渡す - クモのようにコツコツと
ReactのcreateContext
機能もインポートする
import React, { createContext } from 'react';
変数Context
でコンテクスト作成。複数ファイルで使えるようエクスポートする。
export const Context = createContext();
変数data
にステート初期データを設定
const data = { name: '羊', count: 0 }
App
コンポーネントでProvider
を使って子コンポーネントCount
にコンテキストを渡す(初期値はdata
)。
function App() { return ( <div className="App"> <header className="App-header"> <!-- 中略 --> <Context.Provider value={data} > <Count /> </Context.Provider> </header> </div> ); }
Provider
の詳細はこちらを参照
※参考:【React】コンテクスト(Context)でネストされたコンポーネントに値を渡す - クモのようにコツコツと
Count
コンポーネントを修正する
import React, { useState, useEffect, useContext } from 'react'; import { Context } from './App.js';
useState
,useEffect
に加え、useContext
もインポートApp.js
からContext
コンテキストをインポート
function Count() { const context = useContext(Context); const name = context.name; const [count, setCount] = useState(context.count); useEffect(() => { document.title = `React Hooks事始め - ${name}が ${count} 匹`; }); return ( <div className="reactHookTest"> <h2>React Hooks事始め</h2> <p>{name}が {count} 匹</p> <button onClick={() => setCount(count + 1)}> 数える </button> </div> ); } export default Count;
- これまであった変数
data
は不要なので削除する - 変数
context
でuseContext()
を実行(引数はContext
) - 変数
name
でcontext.name
を取得 count
、setCount
のuseState()
の引数をcontext.count
に変更
これまでコンポーネントの中で設定していたステートをuseContext()
によって親コンポーネントApp
からコンテキストを受け取る形に変更した。
ブラウザ挙動
おお、親コンポーネントのコンテキストから受け取った初期値「羊」、「0匹」を表示している!
「数える」ボタンを押すと羊の数が増える!
ページをリロードするとステートの初期値「0匹」に戻る
コード(GitHub)
※参考:GitHub - ryo-i/react-hook-test at 0d008a30779c34e4f2414e19defc2e938d26398c
プレビュー(GitHub Pages)
※参考:React App
最後に
ということで、基本のフック3種類(useState、useEffect、useContext)を体験できましたー。
最後のuseContext
は作っている過程でコンテキストがうまく認識されずTypeErrorなどに苦しみました。いろいろな記事を参考にさせていただき無事成功しましたー。
※参考:React hooksを基礎から理解する (useContext編) - Qiita
※参考:React Context / Hooks 入門
※参考:こんなに簡単なの?React useContextって | アールエフェクト
※参考:React Context で複数のコンポーネント間でデータを共有する|まくろぐ
※参考:useContextのしくみ - Qiita
※参考:5分でわかる useContext の使い方【TypeScriptまで】 - Qiita
※参考:【React hooks】噛み砕いて解説してみた~useContext編~ - Qiita
※参考:React hooksを基礎から理解する (useContext編) - Qiita
フックを使うと関数コンポーネントで状態管理できてシンプルな記述になりますね。
最近のReactはクラスコンポーネントより関数コンポーネントが主流になってきているようです。
フックには他にもいろいろな機能があるようなので徐々に理解を進めていきたいと思います。
さて次は、ReactとFirebaseの連携をやってみる予定です。それではまた!
※参考:Reactを習得するためにやったことまとめ
qiita.com