0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React学習ログ #9|state:コンポーネントのメモリ

Posted at

stateについて

  • フォーム上でタイプすると入力欄が更新される
  • 画像カルーセルで「次」をクリックすると表示される画像が変わる
  • 「購入」をクリックすると買い物かごに商品が入る

などユーザーの操作によって画面上の表示を変えたい際に使用するコンポーネント固有のメモリを指します。

ただの変数ではなく再描画と結びついた変数というイメージになります。

今回の内容に関してはReact学習ログ #5|画面の更新でも触れています。

通常の変数ではうまくいかない例

チュートリアルリンク

「Next」をクリックするとindexの値が連動して変化して、次の画像に切り替わるという機能を実現したいとします。
よくあるページング機能です。

まずは、機能が実現しない例を試してみます。
*data.jsに関してはコードが長くなるため割愛していますが、上記のチュートリアルリンク内で確認出来ます。

src/App.jsx
import { sculptureList } from './data.js';

export default function Gallery() {
  let index = 0;

  function handleClick() {
    index = index + 1;
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleClick}>
        Next
      </button>
      <h2>
        <i>{sculpture.name} </i> 
        by {sculpture.artist}
      </h2>
      <h3>  
        ({index + 1} of {sculptureList.length})
      </h3>
      <img 
        src={sculpture.url} 
        alt={sculpture.alt}
      />
      <p>
        {sculpture.description}
      </p>
    </>
  );
}

画面上で確認します。
画面収録-2026-01-12-9.21.18.gif

分かりづらいですが、「Next」ボタンをクリックしても何も反応していません。

理由は二つあります。

  1. ローカル変数はレンダー間で保持されません。React がこのコンポーネントを次にレンダーするときは、まっさらな状態からレンダーします。過去にローカル変数を変更したことは考慮されません
  2. ローカル変数の変更は、レンダーをトリガしません。新しいデータでコンポーネントを再度レンダーする必要があることに React は気づきません

原因のコードとして、indexがローカル変数になっています。

let index = 0;

そのため、「Next」ボタンをクリックしても単にindex1になるだけで画面上は何も変化しません。
React的には何をしているのか気付いてない状態です。

Reactが監視しているのはstatepropsなので、Reactの管理外のただのJavaScript変数を定義して値を変更しても、何もかわらないのです。

そのため、今回機能を実現するために必要なことは

  1. レンダー間でデータを保持する
  2. 新しいデータでコンポーネントをレンダー(つまり再レンダー)するよう React に伝える

ということです。

state 変数の追加

チュートリアルリンク

state変数を追加するには、ファイルの先頭でReactからuseStateをインポートします。

import { useState } from 'react';

次にindexの定義の記述を変更します。

before
let index = 0;
after
const [index, setIndex] = useState(0);

これでindexはただのローカル変数からstate変数となり、setIndexというセッタ関数も定義されました。

それぞれの役割としては、以下のようになります。

index:レンダー間でデータを保持するstate変数
satIndex:変数を更新し、Reactがコンポーネントを再度レンダーするようにトリガするstateセッタ関数

最後にindexの値を変更していたhandleClickの記述も変更します。

before
function handleClick() {
    index = index + 1;
  }
after
function handleClick() {
  setIndex(index + 1);
}

イメージとしては以下のような動きとなります。

setIndex が呼ばれる

Reactが「stateが変わった」と認識

再レンダリング

新しい index でUIを描画

では実際にコードを書き直してみます。

src/App.jsx
import { useState } from 'react';
import { sculptureList } from './data.js';

export default function Gallery() {
  // 定義の方法を変更
  const [index, setIndex] = useState(0);

  function handleClick() {
    // 際レンダリング用に記述変更
    setIndex(index + 1);
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleClick}>
        Next
      </button>
      <h2>
        <i>{sculpture.name} </i> 
        by {sculpture.artist}
      </h2>
      <h3>  
        ({index + 1} of {sculptureList.length})
      </h3>
      <img 
        src={sculpture.url} 
        alt={sculpture.alt}
      />
      <p>
        {sculpture.description}
      </p>
    </>
  );
}

画面上で確認してみます。

画面収録-2026-01-12-9.58.33.gif

「Next」ボタンのクリックで画面内の描写が切り替わりました。

コンポーネントで複数の state 変数を使う

チュートリアルリンク

コンポーネントでは複数のstateを設定出来ます。

今回は先ほどのページング機能チックなものに、要素の表示非表示を管理するtoggle機能付与してみます。

src/App.jsx
import { useState } from 'react';
import { sculptureList } from './data.js';

export default function Gallery() {
  const [index, setIndex] = useState(0);
  // 初期値をfalseで定義する
  const [showMore, setShowMore] = useState(false);

  function handleNextClick() {
    setIndex(index + 1);
  }

  // toggleボタンのイベント用
  function handleMoreClick() {
    setShowMore(!showMore);
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleNextClick}>
        Next
      </button>
      <h2>
        <i>{sculpture.name} </i> 
        by {sculpture.artist}
      </h2>
      <h3>  
        ({index + 1} of {sculptureList.length})
      </h3>
      <button onClick={handleMoreClick}>
        {/* 値によって表示する文字を変化させている */}
        {showMore ? 'Hide' : 'Show'} details
      </button>
      {/* もし表示状態(値がtrue)であれば要素を表示する*/}
      {showMore && <p>{sculpture.description}</p>}
      <img 
        src={sculpture.url} 
        alt={sculpture.alt}
      />
    </>
  );
}

画面で確認してみます。
画面収録-2026-01-12-11.03.46.gif

このように一つのコンポーネントで違う役割のstateを設定して機能を拡張出来ました。

state は独立しておりプライベート

チュートリアルリンク

stateはコンポーネント内でローカル使用されます。

そのため、親コンポーネント内で子コンポーネントを複数回呼び出した場合stateは共有されません。

まとめ

  • レンダー間で情報を「記憶」しておく必要があるコンポーネントには、state変数を使う
  • state変数は、useStateフックを呼び出すことで宣言される
  • フックはuseから始まる特殊な関数であり、stateなどのReact機能に「接続」できるフックはインポートと似ており、無条件に呼び出す必要がある
  • useStateなどのフックの呼び出しは、コンポーネントのトップレベルか別のフックでのみ有効である
  • useStateフックは、現在のstateとそれを更新する関数の組み合わせを返す複数の state変数を持つことができる
  • 内部でReactはそれらを呼び出し順を用いて対応付ける
  • stateはコンポーネントにプライベートなものである。
  • 2 つの場所でレンダーすると、それぞれのコピーが独立したstateを得る
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?