■P172

src/providers/ThemeProvider.jsx
────────────────
import { createContext } from "react";

export const ThemeContext = createContext({});
────────────────

src/providers/ThemeProvider.jsx
────────────────
import { createContext, useState } from "react";

export const ThemeContext = createContext({});

export const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState("light");

  const handleThemeToggle = () => {
    setTheme((prevTheme) =>
      prevTheme === "light" ? "dark" : "light",
    );
  };

  // valueにはグローバルに共有したい状態と関数を設定
  // 子コンポーネントでグローバルステートを利用するためにProviderでchildrenを囲む
  return (
    <ThemeContext.Provider
      value={{ theme, onThemeToggle: handleThemeToggle }}
    >
      {children}
    </ThemeContext.Provider>
  );
};
────────────────


■P173

────────────────
import { Content } from "./components/Content";
import { Footer } from "./components/Footer";
import { Header } from "./components/Header";
import { ThemeProvider } from "./providers/ThemeProvider";

const App = () => {
  // ThemeProviderのContextにアクセスしたいコンポーネントを囲む
  return (
    <ThemeProvider>
      <Header />
      <Content>メインコンテンツが入ります</Content>
      <Footer />
    </ThemeProvider>
  );
};

export default App;
────────────────


■P174

────────────────
import { useContext } from "react";
import { ThemeContext } from "../providers/ThemeProvider";

export const Header = () => {
  // useContextの引数に参照するContextを指定
  const { theme, onThemeToggle } = useContext(ThemeContext);

  const backgroundColor =
    theme === "light" ? "#00a2d6" : "#1c68b2";

  const buttonLabel =
    theme === "light"
      ? "ダークモードに切り替え"
      : "ライトモードに切り替え";

  return (
    <header
      style={{
        backgroundColor,
        display: "flex",
        justifyContent: "space-between",
        padding: "20px",
      }}
    >
      <p style={{ color: "#fff" }}>グローバルヘッダー</p>
      <button onClick={onThemeToggle}>{buttonLabel}</button>
    </header>
  );
};
────────────────


■P175

────────────────
import { useContext } from "react";
import { ThemeContext } from "../providers/ThemeProvider";

export const Footer = () => {
  // contextからthemeを取得
  const { theme } = useContext(ThemeContext);

  const backgroundColor =
    theme === "light" ? "#ddd" : "#403a3a";

  const textColor = theme === "light" ? "#000" : "#fff";

  return (
    <div
      style={{
        backgroundColor,
        display: "grid",
        placeItems: "center",
        padding: "40px",
      }}
    >
      <p style={{ color: textColor }}>グローバルフッター</p>
    </div>
  );
};
────────────────


■P176

src/components/ThemedBox.jsx
────────────────
import { useContext } from "react";
import { ThemeContext } from "../providers/ThemeProvider";

export const ThemedBox = () => {
  // contextからthemeを取得
  const { theme } = useContext(ThemeContext);

  const backgroundColor =
    theme === "light" ? "#00a2d6" : "#1c68b2";

  return (
    <div
      style={{
        backgroundColor,
        display: "grid",
        placeItems: "center",
        padding: "20px",
      }}
    >
      <p style={{ color: "#fff" }}>
        テーマで色が変わるボックス
      </p>
    </div>
  );
};
────────────────

src/components/Content.jsx
────────────────
import { ThemedBox } from "./ThemedBox";

export const Content = ({ children }) => {
  return (
    <main
      style={{
        backgroundColor: "#fcfcfc",
        display: "grid",
        placeItems: "center",
        gap: "20px",
        padding: "40px",
      }}
    >
      {children}
      <ThemedBox />
    </main>
  );
};
────────────────


■P178

────────────────
const App = () => {
  return (
    <ThemeProvider>
      <OtherProvider>
        <Header />
        <Content>メインコンテンツが入ります</Content>
        <Footer />
      </OtherProvider>
    </ThemeProvider>
  );
};
────────────────

