【React.js】useStateとuseEffectの使い方をご紹介(React hooksを学ぶ)
目次
今回から複数回に分けてReact 16.8から導入された新機能のReact hooksについて、いくつか使い方を紹介していきたいと思います。
今回は、状態管理のフックであるuseStateと副作用の処理のフックであるuseEffectを紹介します。
前回までは、環境構築やコンポーネントとpropsの記事を書きました。
まだご覧でない方はそちらから是非ご覧ください。
【React.js】React.jsとは?環境構築やHello World表示までをご紹介
https://www.dailyupblog.com/web_development/1830/
【React.js】React.jsにおけるコンポーネントとpropsの使い方をご紹介!
https://www.dailyupblog.com/web_development/1860/
それでは早速いきましょう。
React hooksとは?
React hooksとは、React 16.8から追加された、Reactの機能を関数コンポーネントでシンプルに使いやすくしたものです。
React hooksで追加された機能はいくつかあります。
それら機能を下記にまとめました。
useState | 状態管理のフック(stateの保持と更新) |
useEffect | 副作用処理のフック(レンダリング後に実行) |
useContext | Context機能利用のフック(Contextに収容されているデータにアクセス) |
useReducer | 状態管理のフック(useStateと似たような機能) |
useCallback | メモ化したコールバック関数を返すフック |
useMemo | 結果が同等の場合の値を保持し、そこから再取得するフック |
useRef | 要素への参照、値の保持をするフック |
useImperativeHandle | 親コンポーネントから子コンポーネントの関数を呼び出すフック |
useLayoutEffect | 副作用処理のフック(同期効果あり) |
useDeferredValue | 値の更新を遅延させるフック(緊急性の低い値を対象にする) |
useTransition | 次の画面を裏側でレンダリングするフック |
useId | クライアント側とサーバー側で安定した一意のIDを作成するフック |
上記のようにReact hooksにはさまざまな機能が存在します。
これから複数回に分けてこれらを紹介していきます。
今回はuseStateとuseEffectを紹介します。
フック API リファレンス
https://ja.legacy.reactjs.org/docs/hooks-reference.html
useStateについて
useStateは状態管理のフックです。
関数コンポーネントで、stateの保持と更新を行うことができます。
stateとは、コンポーネント内部で保持できる値(状態)のことで、画面上に表示されている値などの状態を指します。
stateの特徴としては、以前紹介したpropsとは異なり、値を後から変更することが可能です。
それでは具体的に使い方を紹介します。
「Components」ディレクトリ内にCounter.tsxというファイルを作成してください。
このファイルに下記のように記述してください。
import { useState } from "react";
const Counter = () => {
const [count, setCount] = useState<number>(0);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
<button onClick={() => setCount(count - 1)}>-1</button>
</div>
)
}
export default Counter;
これは、数値に対して、ボタン押下で1を足したり引いたりする機能を実装したものです。
ここで扱う数値にuseStateを使用しています。
上記の例のようにuseStateの基本形は下記になります。
const [state, setState] = useState<型>(初期値);
上記の説明は下記になります。
state:状態を管理したいstate名
setState:状態を変更するための関数、関数名は基本的にキャメルケースで記述
useStateの型は、genericsにて指定しましょう。
useState関数の引数には、初期値となる値を入れてください。
先ほどの例では、1を足すボタンと1を引くボタンのそれぞれにonClickで「setCount」関数を指定しています。
さて、Counterコンポーネントを呼び出してみます。
src直下のindex.tsxを開き、下記にように書き換えてCounterコンポーネントを呼び出してください。
//省略・・・
import Counter from './components/Counter';
//省略・・・
root.render(
<React.StrictMode>
<Counter />
</React.StrictMode>
);
//省略・・・
ここまでできたら、ブラウザでページを確認してみてください。
下記のように表示されていたらOKです。
プラスとマイナスのボタンを押してみて、上の数値が足されたり引かれたりしたら成功です。
このように、stateを管理できるのがuseStateです。
useEffectについて
useEffectは、副作用の処理のフックです。
具体的には、レンダリングの結果が画面に反映された後に動作させるフックです。
つまるところ、useEffectを使えば、関数の処理をレンダリング後まで遅延させることができるものです。
今回は、関数コンポーネントだけを扱っているため省きますが、クラスコンポーネントでいうところのライフサイクルメソッドにあたります。
実際に例を見てみましょう。
「components」フォルダ内の中にUserInput.tsxファイルを作成してください。
そのファイルの中に下記のように記述してください。
import { useEffect, useState } from 'react';
type Name = {
firstName: string,
lastName: string
}
const UserInput = () => {
const [name, setName] = useState<Name>({firstName: "", lastName: ""})
useEffect(() => {
document.title = `${name.firstName} ${name.lastName}`
},[name])
return(
<>
<p>名前:{name.firstName}{name.lastName}</p>
<input type="text" value={name.firstName} onChange={(e) => setName({...name,firstName: e.target.value})} />
<input type="text" value={name.lastName} onChange={(e) => setName({...name,lastName: e.target.value})} />
</>
)
}
export default UserInput
これは、inputフォームに名前を入力した際に、useEffectを用いて、レンダリング後にタイトルにも入力した名前が代入される処理です。
上記の例でもわかる通り、useEffectは下記のような形で、関数コンポーネント内のトップで宣言します。
useEffect(() => {
//レンダリング後に実行する処理
},[依存する変数の配列])
第二引数には依存する変数を配列形式で記述します。
この第二引数は省略可能ですが、無限ループの危険性があるため、基本的には何かしらは入れます。
今回の例では、nameのstateを配列に入れています。
これにより、nameが更新された時だけ、useEffect関数を実行します。
仮にこの第二引数を省略した場合は、全てのレンダリング後にuseEffect処理を実行させてしまいます。
また、第二引数に空配列[]を入れた場合は、初回レンダリング後のみ処理を実行します。
useEffect内に記述した処理はレンダリング後に実行されるため、今回の例でもuseStateで画面内に入力内容が即時更新されるのに対し、タイトル部分はコンマ数秒遅れて更新されます。
srcフォルダ直下のindex.tsxファイル内を下記のように書き換えてください。
//省略・・・
import UserInput from './components/UserInput';
//省略・・・
root.render(
<React.StrictMode>
<UserInput />
</React.StrictMode>
);
//省略・・・
ブラウザで確認してみてください。
下記のように表示され、入力内容がタイトルに反映されればOKです。
マウントとアンマウント
さて、useEffectの使い方を紹介しましたが、前述の通り、useEffectはクラスコンポーネントでいうところのライフサイクルメソッドに当たります。
このライフサイクルについて考えてみたいと思います。
ライフサイクルとは、マウントからアンマウントまでの一連の流れのことをいいます。
マウントとは、インスタンスとDOMノードの作成、及びそれをDOMツリー(DOMコンテナ)に追加する処理のことです。
簡単に言えば、コンポーネントを画面に表示する際に最初に行われる処理のことを指します。
そして、アンマウントとは、マウントの逆で、DOMツリーからDOMノードを削除する処理のことを指します。
つまり、画面に描画されたコンポーネントを破棄する処理です。
Reactでは、このマウントからアンマウントの一連の流れを繰り返し行うことで、一つのアプリケーションが成り立つわけです。
クリーンアップ
useEffectはレンダリング後に処理を行うフックという説明をしました。
ただ、このuseEffectを使用するには注意が必要で、例えばイベントリスナーやタイマー処理など処理が継続するような時は、次のuseEffectが動作する際に前回のuseEffectの処理とバッティングしてしまう恐れがあります。
なので、新しいuseEffectが実行される際には、前回のuseEffect処理を破棄する必要が出てきます。
そういう時にクリーンアップを使います。
例えば、先ほどの名前入力の例を見てみます。
先ほど紹介した名前入力の例では、フォームに入力が発生するたびに、レンダリングが行われ、一文字一文字が入力される度にuseEffectが実行されていました。
これを、ある程度入力が完了されてから、useEffectが実行されるようにしたいところです。
そのためには、setTimeout関数を使います。
UserInput.tsxファイルを下記のように書き直してください。
import { useEffect, useState } from 'react';
type Name = {
firstName: string,
lastName: string
}
const UserInput = () => {
const [name, setName] = useState<Name>({firstName: "", lastName: ""})
useEffect(() => {
const id = setTimeout(() => {
document.title = `${name.firstName} ${name.lastName}`
}, 1000)
return () => {
clearTimeout(id);
}
},[name])
return(
<>
<p>名前:{name.firstName} {name.lastName}</p>
<input type="text" value={name.firstName} onChange={(e) => setName({...name,firstName: e.target.value})} />
<input type="text" value={name.lastName} onChange={(e) => setName({...name,lastName: e.target.value})} />
</>
)
}
export default UserInput
setTimeoutを用いて、1秒後にuseEffectが実行されるようになっています。
useEffect内にreturnを記述していますが、これがクリーンアップです。
この中でsetTimeoutを強制終了させるclearTimeoutが記述されています。
こうすることで、文字を入力後、1秒以内に再度入力があった際に、前回のuseEffect処理を破棄して、次のuseEffect処理が実行されるようになっています。
これにより、ある程度入力を終えてから1秒後にuseEffectを実行するといった処理が実現できます。
最後に
今回は、React hooksのuseStateとuseEffectの使い方をご紹介しました。
どちらも、React開発でよく使われる機能なので、使い方をしっかりと覚えて使いこなせるようになりたいところです。
React hooksには、他にもたくさんの機能があります。
これから複数回に分けて、このReact hooksを勉強して記事にしていきたいと思うので、是非ご覧ください。
前回:【React.js】React.jsにおけるコンポーネントとpropsの使い方をご紹介!
https://www.dailyupblog.com/web_development/1860/
次回:【React.js】useContext、useReducerの使い方をご紹介(React hooksを学ぶ)
https://www.dailyupblog.com/web_development/1926/