【React】useStateの使い方を分かりやすく解説!

この記事ではReactフックの1つである『useState』について、

  • useStateとは
  • useStateの構文
  • useStateの使い方

などをサンプルコードを用いて分かりやすく説明するように心掛けています。ご参考になれば幸いです。

useStateとは

useStateは、関数コンポーネント内で状態管理を可能にするReactフックの一つです。useStateを用いれば、関数コンポーネントでもクラスコンポーネントのように状態(state)を持たせることができるようになり、関数コンポーネントが内部で保持する状態(state)の保持と更新を行うことができます。

補足

  • useStateは最も利用されているReactフックです。
  • useStateはReact16.8で導入されたフックです。
  • stateはpropsと違って後から変更することが可能です。

useStateの構文

useStateの構文を以下に示します。useStateは初期値initialStateを引数に取り、配列を返します。この配列は現在の状態stateとその状態を更新するための関数setStateを含んでいます。

useStateの構文

const [state, setState] = useState(initialState);
  • initialState
    • 状態(state)の初期値です。この値は関数コンポーネントが最初にマウントされる時に使用されます。
  • state
    • 状態(state)を保持する変数です。
    • 現在の状態(state)の値を示します。
    • setState関数でのみ状態(state)の値を更新することができます。
  • setState
    • 状態(state)を更新するための関数です。
    • この関数に新しい状態(state)の値を渡すことで、状態(state)が更新され、コンポーネントが再レンダリングされます。

状態(state)の更新はsetState関数によってのみ行われます(Stateの箇所はコードによって異なります)。以下に示す方法で状態(state)を更新することができます。

// setState関数の引数に「直接値」を渡す方法
// 例
setState(0); // 数値を渡している
setState(state + 1); // 計算式の結果を渡している
setState(nextState); // 変数に格納されている値を渡している

// setState関数の引数に「関数」を渡す方法
// 例
setState((state) => state + 1);
setState((state) => getNextState(state));

setState関数の引数に「関数」を渡した場合、現在の状態に基づいて次の状態を計算しています(後ほど詳しく解説します)。

また、状態(state)の初期値はuseStateの引数で設定します。以下に示す方法で状態(state)を更新することができます。

// useStateの引数に「直接値」を渡す方法
// 例
useState(0); // 数値を渡している
useState(initialState + 1); // 計算式の結果を渡している
useState(initialState); // 変数に格納されている値を渡している

// useStateの引数に「関数」を渡す方法(関数の返り値が初期値になる)
// 例
useState(() => createInitialState());

補足

状態(state)を保持する変数xxxxxに対して、その状態(state)を更新する関数はsetXxxxxと命名するのが基本です。後ほど説明するサンプルコードでは、状態(state)を保持する変数countに対して、その状態(state)を更新する関数はsetCountと命名しています。

useStateの使い方

以下に示しているのはuseStateを用いた簡単なカウンターアプリのサンプルコードです。このサンプルコードを元にuseStateの使い方を説明します。

import React, { useState } from 'react';

function App() {
  const [count, setCount] = useState(0); // 0はカウントの初期値

  const onButtonClick = () => {
    setCount(count + 1);
  };

  return (
    <>
      <p>カウント回数: {count}</p>
      <button onClick={onButtonClick}>カウント</button>
    </>
  );
}

export default App;

まず、上記のサンプルコードの以下の箇所では、useStateをインポートしています。useStateはインポートしなくても使えますが、インポートしない場合には、useState()React.useState()にする必要があります。

import React, { useState } from 'react';

上記のサンプルコードの以下の箇所では、useStateを使用してcountという状態と、それを更新するsetCount関数を作成しています。また、countは0で初期化しています。

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

上記のサンプルコードの以下の箇所では、変数countの値を取得しています。

<p>カウント回数: {count}</p>

上記のサンプルコードの以下の箇所では、ボタンがクリックした際に、onButtonClick関数が呼ばれており、そのonButtonClick関数の中ではsetCount関数を呼び出しています。setCount関数を呼び出されると、変数countが1増加します。

const onButtonClick = () => {
  setCount(count + 1);
};

実行結果

useStateを用いて作成したカウンターアプリの実行結果

補足

上記のサンプルコードにおいて、setCount(count + 1)setCount(count++)にしても正常に動作せず、ページに正しく反映されません。状態(state)を直接操作することはReactの管理外になっています。そもそも上記のサンプルコードでは、変数countをconst宣言しているので、「++」演算子による操作は定数の規約違反になります。

useStateの注意点

useStateを用いる上での注意点を以下に示します。

  • 状態(state)は非同期に更新されるので注意

上記の注意点についてこれから説明します。

状態(state)は非同期に更新されるので注意

先ほど示したカウンターアプリでは、状態(state)の更新は以下の2つの方法で行うことができます。

// useStateで作成したsetCount関数の引数に「直接値」を渡す方法
setCount(count + 1)
// useStateで作成したsetCount関数の引数に「関数」を渡す方法
setCount((count) => count + 1);

上記の方法において、setCount関数を複数呼び出した場合の動作に違いがあるので注意してください。以下のサンプルコードでは、先ほど示したカウンターアプリにて、setCount(count + 1)を2回呼び出すようにしました。

import React, { useState } from 'react';

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

  const onButtonClick = () => {
    setCount(count + 1);
    setCount(count + 1); // ★setCount関数を2回呼び出している
  };

  return (
    <>
      <p>カウント回数: {count}</p>
      <button onClick={onButtonClick}>カウント</button>
    </>
  );
}

export default App;

上記のサンプルコードではsetCount(count + 1)を2回呼び出しているのですが、カウントボタンを押しても+1しかされません。それは、Reactでは状態(state)を非同期に更新しているからです。具体的には、状態(state)が更新されるのは、イベントハンドラー(クリック等のイベントによって呼び出されるコードや関数のこと)の実行完了後であり、状態(state)がコンポーネントの再描画までに変更されることはありません。そのため、1回目のsetCount(count + 1)と2回目のsetCount(count + 1)の間では、状態(ここではcount)の値は変化しないのです。

一方、以下のサンプルコードでは、setCount((count) => count + 1)を2回呼び出しています。

import React, { useState } from 'react';

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

  const onButtonClick = () => {
    setCount((count) => count + 1);
    setCount((count) => count + 1); // ★setCount関数を2回呼び出している
  };

  return (
    <>
      <p>カウント回数: {count}</p>
      <button onClick={onButtonClick}>カウント</button>
    </>
  );
}

export default App;

setCount((count) => count + 1)のように、useStateで作成したsetCount関数の引数に「関数」を渡すと、先ほどの問題が解決されます。setCount関数の引数に「関数」を渡した場合、現在の状態に基づいて次の状態を計算しています。そのため、setCount((count) => count + 1)を2回呼び出した場合、+2されます。

一般的には、useStateで作成する関数(ここではsetCount関数)の引数に「関数」を渡す方がバグが起きにくく、安全で確実に状態を更新できるのでお勧めです。

本記事のまとめ

この記事ではReactフックの1つである『useState』について、以下の内容を説明しました。

  • useStateとは
  • useStateの構文
  • useStateの使い方

お読み頂きありがとうございました。