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などサーバー側でも描画される構成では、サーバーにwindowやlocalStorageがないため、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の読み取りをコンポーネント本体で行っているため、再レンダリングのたびにgetItemとJSON.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が解決するまでの間は、上位のSuspenseのfallbackが表示されます。
React V19から追加されたuseフックについては、useフックについてを参考にしてみてください。