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 でデータ保存する方法X選

Last updated at Posted at 2025-03-20

はじめに

やっていること:
フロントエンド React で WEB サービスを書いている。
やりたいこと:
useState で管理しているデータを URL 遷移先に渡したい。

codesandbox.io で色々動かしてみました。
スーパーロング羅列型記事になってしまったので、目次のみご覧いただけると嬉しいです👼

ストレージを使う

  • localStorage
  • sessionStorage
const data = { message: 'こんにちは', id: 123 };
localStorage.setItem('sharedData', JSON.stringify(data));

const data = JSON.parse(localStorage.getItem('sharedData') || '{}');

クッキーを使う

// index.js
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { CookiesProvider } from "react-cookie";

import App from "./App";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <CookiesProvider defaultSetOptions={{ path: "/" }}>
    <StrictMode>
      <App />
    </StrictMode>
  </CookiesProvider>
);

// App.tsx
import React, { useState } from "react";
import { useCookies } from "react-cookie";

interface CookieValues {
  name?: string;
}

function App(): React.ReactElement {
  const [cookies, setCookie] = useCookies<CookieValues>(["name"]);
  const [inputValue, setInputValue] = useState(cookies.name || "");

  // input フォーム管理用の関数。クッキーには関係なし。
  const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setInputValue(event.target.value);
  };

  // クッキー保存用の関数
  const handleSubmit = () => {
    setCookie("name", inputValue); // 入力された名前をクッキーに保存
  };

  return (
    <div>
      <input
        type="text"
        value={inputValue}
        onChange={handleInputChange}
        placeholder="名前を入れてください"
      />
      <button onClick={handleSubmit}>Save Name</button>

      {/* クッキーに保存された名前があれば表示 */}
      {cookies.name && <h1>Hello {cookies.name}!</h1>}
      {console.log("クッキー", cookies)}
    </div>
  );
}

export default App;

react-router-dom 系を使う

ヒストリーを使う バージョン6で廃止

  • useHistory
  • useLocation

ステートを使う

  • useNavigate
  • useLocation
// App.tsx
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import Home from "./Home";
import About from "./About";

export default function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Router>
  );
}

// Home.tsx
import { useNavigate } from "react-router-dom";

function Home() {
  const navigate = useNavigate();

  const goToAboutPage = () => {
    // ページ遷移で情報を渡している部分
    navigate("/about", { state: { message: "これがHomeからAboutに送る情報です。" } });
  };

  return <button onClick={goToAboutPage}>アバウトページに行く</button>;
}

export default Home;

// Abouts.tsx
import { useLocation } from "react-router-dom";

function About() {
  // ページ遷移で情報を受け取っている部分
  const location = useLocation();
  const message = location.state || "情報の送信がされていません。";
  
  return (
    <p>{message}</p>
  );
}

export default About;

URLを使う①

  • useNavigate
  • useParams
  • (encodeURIComponent、decodeURIComponent)
// App.tsx
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import Home from "./Home";
import About from "./About";

export default function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        {/* URL を変形 */}
        <Route path="/about/:userName/:id" element={<About />} />
      </Routes>
    </Router>
  );
}

// Home.tsx
import { useNavigate } from "react-router-dom";

function Home() {
  const navigate = useNavigate();

  const userName = "太郎";
  // const encodedUserName = encodeURIComponent(userName); // エンコードしてもOK
  const id = 1;
  const goToAboutPage = () => {
    navigate(`/about/${userName}/${id}`);
  };

  return <button onClick={goToAboutPage}>アバウトページに行く</button>;
}
export default Home;

// About.tsx
import { useParams } from "react-router-dom";

function About() {
  const { userName, id } = useParams();
  // const decodedUserName = decodeURIComponent(userName); // デコードする

  return (
    <div>
      <h1>受信データ</h1>
      <p>名前: {userName || "未設定"}</p>
      <p>ID: {id || "未設定"}</p>
    </div>
  );
}
export default About;

URLを使う②(①とほぼ同じ)

  • useNavigate
  • useSearchParams
// App.tsx
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import Home from "./Home";
import About from "./About";

export default function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        {/* URL 普通でOK */}
        <Route path="/about" element={<About />} />
      </Routes>
    </Router>
  );
}

// Home.tsx
import { useNavigate } from "react-router-dom";

function Home() {
  const navigate = useNavigate();

  const goToAboutPage = () => {
    navigate("/about?userName=太郎&id=1");
  };

  return <button onClick={goToAboutPage}>アバウトページに行く</button>;
}

export default Home;

// About.tsx
import { useSearchParams } from "react-router-dom";

function About() {
  const [searchParams] = useSearchParams();
  const userName = searchParams.get("userName");
  const id = searchParams.get("id");

  // ↑ 上3行コメントアウトして ↓ 下4行コメント外したら useLocation でも同じことができる
  // const location = useLocation();
  // const queryParams = new URLSearchParams(location.search);
  // const userName = queryParams.get("userName");
  // const id = queryParams.get("id");

  return (
    <div>
      <h1>受信データ</h1>
      <p>名前: {userName || "未設定"}</p>
      <p>ID: {id || "未設定"}</p>
    </div>
  );
}

export default About;

コンテキストを使う

  • createContext
  • useContext
// App.tsx
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import { DataProvider } from "./DataContext"; // DataProviderをインポート
import Home from "./Home";
import About from "./About";

export default function App() {
  return (
    {/* DataProvider で全体をラップ */}
    <DataProvider>
      <Router>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
        </Routes>
      </Router>
    </DataProvider>
  );
}

// DataContext.tsx
import { createContext, useState, ReactNode } from "react";

const DataContext = createContext<
  | {
      data: string | null;
      setData: React.Dispatch<React.SetStateAction<string | null>>;
    }
  | undefined
>(undefined);

function DataProvider({ children }: { children: ReactNode }) {
  const [data, setData] = useState<string | null>(null);

  return (
    <DataContext.Provider value={{ data, setData }}>
      {children}
    </DataContext.Provider>
  );
}

export { DataContext, DataProvider };

// Home.tsx
import React, { useContext } from "react";
import { useNavigate } from "react-router-dom";
import { DataContext } from "./DataContext";

function Home() {
  const { setData } = useContext(DataContext);
  const navigate = useNavigate();

  const goToAboutPage = () => {
    setData({ id: 123, name: "example" });
    navigate("/about");
  };

  return <button onClick={goToAboutPage}>アバウトページに行く</button>;
}

export default Home;

// About.tsx
import React, { useContext } from "react";
import { DataContext } from "./DataContext";

function About() {
  const context = useContext(DataContext);
  const data = context?.data || { name: "未設定", id: "未設定" };

  return (
    <div>
      <h1>受信データ</h1>
      <p>名前: {data.name}</p>
      <p>ID: {data.id}</p>
    </div>
  );
}

export default About;

zustand を使う

  • zustand
// store.js
import { create } from "zustand";

// Zustandストアの作成
const useStore = create((set) => ({
  data: { id: null, name: "" },
  setData: (newData) => set({ data: newData }),
}));

export default useStore;

// Home.js
import React from "react";
import { useNavigate } from "react-router-dom";
import useStore from "./store";

function Home() {
  const setData = useStore((state) => state.setData);
  const navigate = useNavigate();

  const goToAboutPage = () => {
    setData({ id: 123, name: "example" }); // Zustandでデータを更新
    navigate("/about");
  };

  return <button onClick={goToAboutPage}>アバウトページに行く</button>;
}

export default Home;

// About.js
import React from "react";
import useStore from "./store";

function About() {
  const data = useStore((state) => state.data); // Zustandからデータを取得

  return (
    <div>
      <h1>受信データ</h1>
      <p>名前: {data.name}</p>
      <p>ID: {data.id}</p>
    </div>
  );
}

export default About;

react-query を使う

// App.tsx
import React from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import { QueryClient, QueryClientProvider } from "react-query";
import Home from "./Home";
import About from "./About";

// React Query のクライアントを作成
const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Router>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
        </Routes>
      </Router>
    </QueryClientProvider>
  );
}

export default App;

// Home.js
import React from "react";
import { useQuery } from "react-query";
import { useNavigate } from "react-router-dom";

// API呼び出し用の関数
const fetchUserData = async () => {
  const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
  if (!response.ok) {
    throw new Error("ネットワークエラー");
  }
  return response.json();
};

function Home() {
  const navigate = useNavigate();

  // useQuery を使用して API からデータを取得
  const { data, isLoading, error } = useQuery("userData", fetchUserData);

  if (isLoading) {
    return <div>データ読み込み中...</div>;
  }

  if (error) {
    return <div>エラーが発生しました: {error.message}</div>;
  }

  // 取得したデータを表示
  return (
    <div>
      <p>名前: {data.name}</p>
      <p>メール: {data.email}</p>
    </div>
  );
}

export default Home;

Redux を使う

// App.tsx
import React from "react";
import { Provider } from "react-redux";
import { store } from "./store";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import Home from "./Home";
import About from "./About";

function App() {
  return (
    <Provider store={store}>
      <Router>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
        </Routes>
      </Router>
    </Provider>
  );
}

export default App;

// store.js
import { configureStore, createSlice } from "@reduxjs/toolkit";

const initialState = { name: "なまえ", id: "1" };

const userSlice = createSlice({
  name: "user",
  initialState,
  reducers: {
    setUser: (state, action) => {
      state.name = action.payload.name;
      state.id = action.payload.id;
    },
  },
});

export const { setUser } = userSlice.actions;
export const store = configureStore({
  reducer: {
    user: userSlice.reducer,
  },
});

// Home.tsx
import React from "react";
import { useDispatch } from "react-redux";
import { setUser } from "./store";
import { useNavigate } from "react-router-dom";

function Home() {
  const dispatch = useDispatch();
  const navigate = useNavigate();

  const data = { name: "なまえ", id: "1" };

  const goToAboutPage = () => {
    dispatch(setUser(data));
    navigate("/about");
  };

  return (
    <div>
      <button onClick={goToAboutPage}>アバウトページに行く</button>
    </div>
  );
}

export default Home;

// About.tsx
import React from "react";
import { useSelector } from "react-redux";

function About() {
  const user = useSelector((state) => state.user);

  return (
    <div>
      <p>名前: {user.name}</p>
      <p>ID: {user.id}</p>
    </div>
  );
}

export default About;

その他調べきれなかったもの

[そもそも React ページ遷移に関連するか微妙なもの]

  • POSTリクエストを使ってバックエンドにデータを送信&DBに保存
  • 親コンポーネントで管理

[ブラウザの機能使う系]

  • ブラウザのカスタムイベントを使う
  • グローバルな window オブジェクトにデータを一時的に保存
  • window.location.hashを使う
  • WebSocketやカスタムイベントを使う

[DB使う系]

  • シングルトン風のオブジェクトを作りデータを一時的に保存
  • IndexedDBを使う

[パッケージインストール系]

  • cache.writeQuery、cache.readQueryを使う
  • jotaiを使う
  • valtioを使う
  • mobxを使う
  • recoilを使う
  • xstateを使う
  • remixを使う
  • nanostoresを使う

おわりに

((( えっ、React の SPA とかって何? )))
URL でページ遷移させたらダメってこと?

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?