Reactは、ユーザーインターフェースの構築に使用される人気のJavaScriptライブラリです。フックを含む幅広い機能で開発者を支援しアプリケーションの構築を簡単かつ効率的なものにしてくれます。

最もよく使われるフックの1つがuseStateです。これを使うことで関数コンポーネントで手軽に状態(state)を扱えます。

この記事では、useStateの用途と実装のベストプラクティスについて詳しくご紹介します。

useStateフックとは

useStateの詳細に進む前に、まずフックとは何か、そして従来のクラスコンポーネントとどう違うのかを理解しましょう。

フックとは、stateやその他のReactの機能を関数コンポーネントで使用できるようにするものです。

クラスコンポーネントとは異なり、関数コンポーネントはstate(状態)を保持する機能を内蔵していません。そこでuseStateのようなフックを使うことで、関数コンポーネントにstateを追加できます。

useStateフックの背景

React 16.8でuseStateフックが導入される以前、関数コンポーネントそのものはstateを保持することができませんでした。その代わりに、アプリケーションのstateを管理するために、クラスコンポーネントやReduxのような外部の状態管理ライブラリを使わなければなりませんでした。

クラスコンポーネントや外部の状態管理ライブラリは今でも有効な選択肢ですが、useStateフックの導入により、関数コンポーネントにstateを追加するのが非常に簡単になり、React API全体がシンプルになっています。

useStateフックによって、あらゆるケースで関数コンポーネントを使用できるようになり、必要な定型コードの量が減り、アプリケーションのstateを評価しやすくなりました。

useState構文の構成要素

useStateフックは、引数として初期のstateの値を取り、現在のstateの値と(状態の値を変更するための)関数の2つの要素を配列として返します。

以下の構文を見ると理解できるはずです。

const [state, setState] = useState(initialState);
  • state:現在のstateの値
  • setState:stateの値を変更するのに使う関数
  • initialState:宣言するstateの変数の初期値(任意であり値を指定しないと─※推奨されないものの─自動でundefinedに設定される)

useStateフックを使用する場合には、stateの値が変更されるたびにReactが自動でコンポーネントを再レンダリングするので、この仕様は念頭に置くようにしてください。

useStateフックの使い方

useStateを実装するには、関数を呼び出し、引数として初期値を渡します。関数は、現在のstateの値とstateを更新する関数を含む配列を返します。

以下は、useStateを使って単純なカウンターを管理する例です。

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

export default Counter;

この例では、useStateフックを使って、countの状態を0に初期化します。次に、現在のカウント値とボタンをレンダリングします。ボタンがクリックされると(setCount関数でアロー関数を追加したため)カウントの値が増加します。

<button onClick={() => setCount(count + 1)}>Click me</button>

useStateフックを使用する際は、Reactライブラリからインポートすることを忘れないことが重要です。インポートを忘れると、フックを使おうとしてもエラーになる可能性が高くなります。以下に、useStateをインポートする方法を示します。

import { useState } from 'react';

import文にuseStateを含めることで、コンポーネントでuseStateフックを使いたいことをReactに伝えています。

ReactのuseStateフックであらゆるデータ型を使う

ReactのuseStateフックは、文字列や数値のデータ型だけを管理するものではありません。配列、オブジェクト、ブール値など、さまざまなデータ型の状態を管理することができます。

useStateフックで配列を使う

useStateフックを使って、Reactで配列を管理することができます。例えば、果物の配列があれば、その配列でstateを初期化可能です。

import { useState } from 'react';

function App() {
  const [list, setList] = useState(['apple', 'banana', 'orange']);

  const addToList = () => {
    const newItem = 'grape';
    setList([...list, newItem]);
  };

  return (
    <div>
      <ul>
        {list.map((item) => (
          <li key={item}>{item}</li>
        ))}
      </ul>
      <button onClick={addToList}>項目を追加</button>
    </div>
  );
}

export default App;

この例では、useStateを使ってlistという名前の配列を管理しています。 初期の状態には、3つの項目(applebananaorange)を含む配列が設定されています。

また、スプレッド演算子(...) ですべての配列の中身をコピーし、「項目を追加」ボタンがクリックされるたびに、listの配列に新しくgrapeを追加するaddToList関数も作成しました。

この操作をスタンドアロンの関数として作成し、「Add Item」ボタンに追加されたonClickメソッドを通して呼び出すために、ここでは、アロー関数を使用したインラインでのstate更新を行っていません。

useStateフックでオブジェクトを使用する

useStateフックを使って、Reactでオブジェクトを管理することもできます。以下はその例です。

import { useState } from 'react';

function App() {
  const [user, setUser] = useState({ name: 'John', age: 30 });

  const handleClick = () => {
    setUser({ ...user, age: user.age + 1 });
  };

  return (
    <div>
      <p>Name: {user.name}</p>
      <p>Age: {user.age}</p>
      <button onClick={handleClick}>年齢を増加</button>
    </div>
  );
}

export default App;

この例では、useStateを使ってuserというオブジェクトを管理しています。userの初期状態は、nameageの2つのプロパティを含むオブジェクトに設定されています。

また、すべてのオブジェクトデータをスプレッド演算子 (...) でコピーし、「年齢を増加」ボタンがクリックされるたびにユーザーの年齢を1増加させる関数handleClickもあります。

このオブジェクトを変数に代入し、その変数を初期化できるという特徴を知っておくことが重要です。これは、大きなオブジェクトや配列があり、理解しやすいコードを書きたい場合に便利です。

import { useState } from 'react';
// Declare data
let userData = {
    name: 'John',
    age: 30,
};

function App() {
    const [user, setUser] = useState(userData);

    const handleClick = () => {
        setUser({ ...user, age: user.age + 1 });
    };

    return (
        <div>
            <p>Name: {user.name}</p>
            <p>Age: {user.age}</p>
            <button onClick={handleClick}>Increase Age</button>
        </div>
    );
}

export default App;

useStateフックでブール値を使う

useStateフックを使って、Reactでブール値を管理することもできます。以下はその例です。

import { useState } from 'react';

function App() {
  const [isOn, setIsOn] = useState(false);

  const toggleSwitch = () => {
    setIsOn(!isOn);
  };

  return (
    <div>
      <p>The switch is {isOn ? 'on' : 'off'}</p>
      <button onClick={toggleSwitch}>トグルスイッチ</button>
    </div>
  );
}

export default App;

この例では、useStateを使って、isOnというブール値を管理しています。初期のisOnのstateはfalseに設定されています。また、「トグルスイッチ」ボタンがクリックされるたびにisOnの値を切り替える関数toggleSwitchがあります。

この機能は、プロジェクトでダークモードとライトモードの切り替えを管理するのに利用できます。

最後に、フォームを使ったuseStateフックの高度な使い方をご紹介しましょう。

useStateフックとフォーム

Reactでフォームデータとユーザーによる入力データを扱うには、stateを使うことができます。これは、フォーム要素の現在の値を追跡するのに使われます。テキスト入力やドロップダウンからのオプション選択など、すべてのユーザーインタラクションを追跡し、最後に新しい値でstateを更新します。

ここでは、useStateを使ってシンプルなフォームを作成する例を示します。

import { useState } from 'react';

let formStateData = {
    firstName: '',
    lastName: '',
    email: '',
};

function Form() {
    const [formData, setFormData] = useState(formStateData);

    const handleInputChange = (event) => {
        const { name, value } = event.target;
        setFormData({ ...formData, [name]: value });
    };

    const handleSubmit = (event) => {
        event.preventDefault();
        console.log(formData);
    };

    return (
        <form onSubmit={handleSubmit}>
            <input
                type="text"
                name="firstName"
                value={formData.firstName}
                onChange={handleInputChange}
            />
            <input
                type="text"
                name="lastName"
                value={formData.lastName}
                onChange={handleInputChange}
            />
            <input
                type="email"
                name="email"
                value={formData.email}
                onChange={handleInputChange}
            />
            <button type="submit">Submit</button>
        </form>
    );
}

export default Form;

この例では、まず、firstNamelastNameemailフィールドにデフォルト値を指定して、フォームで収集するデータのオブジェクトを作成します。そして、useStateを使ってformDataのstateを初期化します。

let formStateData = {
    firstName: '',
    lastName: '',
    email: '',
};

const [formData, setFormData] = useState(formStateData);

次に、フォームフィールドが変更されるたびにformDataのstateを更新するhandleInputChange関数を定義します。

const handleInputChange = (event) => {
    const { name, value } = event.target;
    setFormData({ ...formData, [name]: value });
};

最後に、フォームが送信されたときに現在のフォームデータをコンソールにログ出力するhandleSubmit関数を定義します。

const handleSubmit = (event) => {
    event.preventDefault();
    console.log(formData);
};

useStateのベストプラクティス

useStateフックを使用する際には、よくある間違いを避け、最適なパフォーマンスを確保するためにベストプラクティスに従うことが重要です。以下に、覚えておくべきいくつかのヒントをご紹介します。

  • 深いネストは避ける:複雑なstateデータをひとつのオブジェクトに格納するのではなく、細かく分割して管理しやすくしましょう。
  • 関数を使って更新を行う:以前のstateに基づいてstateを更新する場合は、競合状態を回避し、正しくstateが更新されるように、関数を使用できます。
  • 過剰なstateの更新を避ける:stateの更新にはコストがかかるので、その回数を最小限に抑えるようにしましょう。useEffectフックを使用して更新をバッチ化し、パフォーマンスを最適化することを検討しましょう。
  • 不必要な再レンダリングを避ける:Reactは、コンポーネントのstateやpropsが変更されるたびに再レンダリングします。不要な再レンダリングを避けるには、memo関数を使用することができます。

まとめ

この記事では、useStateフックについて詳しく説明し、関数コンポーネントにstateを追加する方法をご紹介しました。また、useStateをフォーム、配列、オブジェクトで使用する方法を扱い、最適なパフォーマンスを実現するためのベストプラクティスについても取り上げました。このベストプラクティスに従うことで、Reactアプリケーションを効率的でスケーラブル、かつ保守しやすいものに保つことができます。

Reactアプリケーションにぴったりのホスティングソリューションをお探しであれば、Kinstaのアプリケーションホスティングをまずは無料からお試しください。