はじめに
- やっていること:
- フロントエンド 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') || '{}');
クッキーを使う
- react-cookie(js-cookie とか他の選択肢もありそう)
パッケージドキュメント:https://www.npmjs.com/package/react-cookie?activeTab=readme
// 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で廃止
useHistoryuseLocation
ステートを使う
- 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 でページ遷移させたらダメってこと?