こんにちは。 アドベントカレンダー7日目です。 今年はフロントエンドまわりであれこれ開発することが多かったので、振り返りを兼ねてこれからフロントエンド開発を始める方向けに入門編としてお送りしています。 最終日の25日には何かアプリケーションができていることが目標です。
↓最初から読み始めたい方はこちらか↓
おさらい
Visual Studio Codeを使ったReactの開発環境の構築(第1回)から始まり、CSSフレームワークの1つであるtailwindcssを使ったUI開発(第2回)やコンポーネント化(第3回)、フォーマッター(第4回)、Linter(第5回)について学んできました。
エンジニアの7つの道具の1つであるバージョン管理(第6)について学び、GitHubにリポジトリを作成できました。
今回のはなし
第4〜6回まではReactから少し離れて開発サイクルを回していく上で土台となる部分の環境作りを中心にお話ししてきました。またReactに戻っていきます。
Reactを使っているとほぼ確実にお世話になるのが状態管理です。今回は、状態管理についてじっくり学んで理解を深めていきましょう。
状態管理とは
英語ではstateという言葉で使われており、日本語に訳すると「状態」という言葉がよく使われます。Reactでは、コンポーネント内で状態を保持できます。状態を保持することで、コンポーネントを再描画できます。
https://ejje.weblio.jp/content/state
コンポーネントが再描画できると聞いてもあまりイメージしにくいので、サンプルコードで動きを確認してみましょう。
ボタンをクリックしてカウントアップする
百聞は一見にしかずということで、ボタンをクリックしてカウントアップするサンプルコードを作成してみます。
"use client"; import { useState } from "react"; export default function Home() { const [count, setCount] = useState(0); return ( <div className="p-5"> <div> <p className="text-2xl">現在のカウントは、「{count}」です。</p> </div> <div> <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" onClick={() => setCount((prevCount) => prevCount + 1)} > Button </button> </div> </div> ); }
通常の変数を使って単純にカウントアップ処理だけを書いたプログラムでは、ボタンをクリックしても画面上のカウントは変わりません。console.logなどを用いて値を確認すると、変数の値は変わっていることが確認できます。 ブラウザ上の画面に表示されている値を変更するためには、何らかの方法を使って画面を再描画する必要があります。
Reactには、画面を再描画するための仕組みが用意されています。それが、状態管理(state)です。
状態管理をするための1番基本的な方法は、useState
を使うことです。
const [count, setCount] = useState(0);
useState
は、配列を返します。配列の1番目の要素は、状態の値を取得するための変数です。配列の2番目の要素は、状態の値を更新するための関数です。
ポイント(その1)
setCount(value)
のような形で状態を更新した場合は、次のレンダリング後にuseStateが値を返すまで更新されません。
そのため、直後に変数を参照したとしても値は更新前の状態のままです。
function handleClick() { setCount(3); console.log(count); // 0 }
連続的にsetCountを実行したとしても、最後にrenderされた時点での値が返されます。
function handleClick() { setCount(count + 1); // setCount(0 + 1) setCount(count + 1); // setCount(0 + 1) setCount(count + 1); // setCount(0 + 1) }
しかし、2つ目の方法には解決方法があります。(prev) => {...}
のような更新用の関数を渡すことで、直前の値を参照できます。
function handleClick() { setCount(prevCount => prevCount + 1); // setCount(0 => 1) setCount(prevCount => prevCount + 1); // setCount(1 => 2) setCount(prevCount => prevCount + 1); // setCount(2 => 3) }
ポイント(その2)
useState(initialValue)
のように初期化できます。直接0
やapple
などのように直接値を設定することもあれば、複雑な処理を関数として渡すこともできます。
重い複雑な処理を書いた関数を渡すと、毎回レンダリングされるたびにその関数が実行されてしまいます。関数の渡し方を工夫するだけで、初回レンダリングのみ実行されるようにできます。
毎回実行される書き方
createInitialCount()
のように書いて関数の実行結果をuseState(...)
に渡すと、毎回実行されてしまいます。
const [count, setCount] = useState(createInitialCount());
初回のみ実行される書き方
先ほどの例と似ていますがcreateInitialCount()
とcreateInitialCount
は違います。createInitialCount
のように記述することで、関数自体を渡すことができます。関数自体を渡すことで、初回のみ実行されるようになります。
const [count, setCount] = useState(createInitialCount);
おわりに
今回は、基本的な状態管理について学びました。状態管理は、Reactを使っているとほぼ確実に使う機能です。状態管理を理解していないと、期待したように値が更新されず、調査に時間がかかってしまうことがあります。また、不必要なレンダリングが発生してしまうことで、全体的にパフォーマンスが低下してしまうこともあります。
今回は紹介できませんでしたが、状態管理が複雑になってくるとReduxなどのライブラリを活用することもあります。