React

【React】でセッションストレージやローカルストレージのデータを取得する方法

ReactでlocalStorage・sessionStorageの値を読む最短の方法は、useStateの初期値を関数で渡すlazy initializerです。保存はuseEffectで行います。カート機能での実例と、React 19のuseでPromiseを読む書き方も紹介します。

この記事の目次
  1. Reactでセッションストレージのデータを取得する方法
  2. App.jsなどルートのjsでそのままセッションストレージのデータを取得する場合
  3. React 19で非同期データを取得する最新の方法

Reactでセッションストレージやローカルストレージのデータを取得する方法を紹介します。
セッションストレージでもローカルストレージでもやり方はまったく同じため、以下の例では、セッションストレージで紹介します。
localStorageの扱いが不安な場合は、【JavaScritpt】ブラウザ保存領域にデータを格納するlocalStorageの使い方を参考にしてみてください。

Reactでセッションストレージのデータを取得する方法

結論から書くと、ReactでlocalStorageやsessionStorageの値を読むには、useStateの初期値を関数で渡す(lazy initializer)のがいちばん短く、再レンダリングのたびにストレージを読み直さずに済みます。
保存したいときは、その状態を依存配列に入れたuseEffectの中でsetItemします。

import { useEffect, useState } from 'react';

function Counter() {
  // 初回レンダリング時だけlocalStorageを読む。なければ0
  const [count, setCount] = useState(() => {
    const saved = localStorage.getItem('count');
    return saved !== null ? JSON.parse(saved) : 0;
  });

  // countが変わるたびにlocalStorageへ書き戻す
  useEffect(() => {
    localStorage.setItem('count', JSON.stringify(count));
  }, [count]);

  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

ストレージに入るのは文字列だけなので、配列やオブジェクトはJSON.stringifyで保存し、JSON.parseで戻します。
sessionStorageにしたい場合は、localStorageの部分を書き換えるだけです。タブを閉じると消えてよいならsessionStorage、次に開いたときも残したいならlocalStorageを選びます。
Next.jsなどサーバー側でも描画される構成では、サーバーにwindowlocalStorageがないため、lazy initializerの中でtypeof window !== 'undefined'を確認するか、useEffectの中で読み込みます。

ここからは、実際のカート機能で使った少し長い例を見ていきます。

<div class="contents__container l-contents" id="mainContent"></div>

まずはマウントするためのhtmlを準備します。

スポンサーリンク

App.jsなどルートのjsでそのままセッションストレージのデータを取得する場合

import React, { useEffect, useState } from 'react';
import { createRoot } from 'react-dom/client';
import Catalog from './catalog/Catalog';
import Sidebar from './catalog/Sidebar';
import OpenDownloadModal from './catalog/OpenDownloadModal';

function App() {
  const [cartItems, setCartItems] = useState([]); // カートアイテムの状態
  const [open, setOpen] = useState(false); // モーダルボタンの状態
  const [show, setShow] = useState(false); // サイドバーの表示状態

  // セッションストレージから保存されたカタログIDを取得し、重複を取り除く
  const storedCatalogIdsJSON = sessionStorage.getItem('catalogIds');
  const storedCatalogIds = storedCatalogIdsJSON
    ? JSON.parse(storedCatalogIdsJSON).map(id => parseInt(id))
    : [];
  const uniqueCatalogIds = [...new Set(storedCatalogIds)];

  // 初回レンダリング時にセッションストレージからカートアイテムを取得して初期化
  useEffect(() => {
    setCartItems(uniqueCatalogIds);
  }, []);

  // カートアイテムが変更されたときの処理
  useEffect(() => {
    if (cartItems.length === 0) {
      setOpen(false); // カートが空ならモーダルを閉じる
      setShow(false); // サイドバーを非表示にする
    } else {
      setOpen(true); // カートにアイテムがあればモーダルボタンを開く
    }
  }, [cartItems]);

  // カートにアイテムを追加する関数
  const handleAddToCart = catalogId => {
    setCartItems(prevCartItems => [...prevCartItems, catalogId]);
    setOpen(true); // モーダルボタンを開く
    setShow(true); // サイドバーを表示する
  };

  // カートからアイテムを削除する関数
  const handleRemoveFromCart = catalogId => {
    setCartItems(prevCartItems =>
      prevCartItems.filter(item => {
        return parseInt(item, 10) !== parseInt(catalogId, 10);
      })
    );
  };

  return (
    <>
      {/* Catalog コンポーネントに cartItems を渡す */}
      <Catalog cartItems={cartItems} onAddToCart={handleAddToCart} />
      {/* Sidebar コンポーネントに cartItems を渡す */}
      <Sidebar
        cartItems={cartItems}
        onRemoveFromCart={handleRemoveFromCart}
        show={show}
        toggleMenu={() => {
          setShow(!show);
        }}
      />
      {cartItems.length !== 0 && (
        <OpenDownloadModal
          cartItems={cartItems}
          open={open}
          toggleMenu={() => {
            setShow(!show);
          }}
        />
      )}
    </>
  );
}

const domNode = document.getElementById('mainContent');
const root = createRoot(domNode);
root.render(<App />);

まず、セッションストレージから取得したデータを状態として扱うためにuseStateを使います。
上の例ではcartItemsです。
そして、useEffectの第二引数を空にして、初回のレンダリング時にセッションストレージからカートアイテムを取得して初期化します。
これで、sessionStorageからデータを取得できます。
cartItemsが更新されるたびに何かをしたければ、上のようにuseEffectを別に作成して第二引数にcartItemsを入れます。
なお、この例はsessionStorageの読み取りをコンポーネント本体で行っているため、再レンダリングのたびにgetItemJSON.parseが走ります。この記事の冒頭で紹介したlazy initializerにまとめると、読み取りは初回だけになります。

new Setで重複データを削除していますが、より詳しく知りたい場合は、JavaScriptの重複を消すことができるSetの使い方と具体例を参考にしてみてください。
あとは、自由に使いたいようにすればOKです。

上記の例では、セッションストレージに保存されたデータをReactの状態管理に取り込む方法を示しました。
React 19ではuseが追加され、Promiseを直接読み取れるようになりました。ストレージの読み取り自体は同期処理なので必須ではありませんが、ストレージの値をもとにサーバーから追加データを取ってくるような場面では次のように書けます。

React 19で非同期データを取得する最新の方法

import { Suspense, use } from 'react';
import { createRoot } from 'react-dom/client';
import Catalog from './catalog/Catalog';

async function fetchSessionStorageData() {
  const storedCatalogIdsJSON = sessionStorage.getItem('catalogIds');
  const storedCatalogIds = storedCatalogIdsJSON
    ? JSON.parse(storedCatalogIdsJSON).map((id) => parseInt(id, 10))
    : [];
  return [...new Set(storedCatalogIds)];
}

// Promiseはコンポーネントの外で1回だけ作る
// レンダリングのたびに作り直すと、useが毎回新しいPromiseを待ち続けて描画が終わらない
const cartItemsPromise = fetchSessionStorageData();

function Cart() {
  const cartItems = use(cartItemsPromise);

  return <Catalog cartItems={cartItems} />;
}

function App() {
  return (
    <Suspense fallback={<p>読み込み中...</p>}>
      <Cart />
    </Suspense>
  );
}

const domNode = document.getElementById('mainContent');
const root = createRoot(domNode);
root.render(<App />);

useに渡すPromiseは、レンダリングの外で作るか、キャッシュして同じものを渡す必要があります。Promiseが解決するまでの間は、上位のSuspensefallbackが表示されます。

React V19から追加されたuseフックについては、useフックについてを参考にしてみてください。

スポンサーリンク