본문 바로가기

Front-End/React

React #18 (훅, 상태관리 - useContext)

반응형

 

useContext

React의 Context API를 사용하여 전역 상태를 관리할 수 있게 해주는 훅.

props로 일일이 전달하지 않고도 컴포넌트 트리 어디서든 데이터에 접근할 수 있다.

 

 

 

 

1. 다크모드 토글

더보기

src/contexts/ThemeContext.tsx

import { createContext, useContext, useState, ReactNode } from "react";

type Theme = "light" | "dark";

type ThemeContextType = {
  theme: Theme;
  toggleTheme: () => void;
};

export const ThemeContext = createContext<ThemeContextType | undefined>(
  undefined
);

type ThemeProviderProps = {
  children: ReactNode;
};

export default function ThemeProvider({ children }: ThemeProviderProps) {
  const [theme, setTheme] = useState<Theme>("light");

  const toggleTheme = () => {
    setTheme(theme === "light" ? "dark" : "light");
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

 

src/components/Header.tsx

// Header, Content, Footer 컴포넌트에서 각각 다른 텍스트 표시
// 언어 변경 버튼 구현

import { useContext } from "react";


export default function Header() {
  const themeContext = useContext(ThemeContext);
  if (!themeContext) throw new Error("ThemeContext not found");

  const { theme, toggleTheme } = themeContext;


  return (
    <header
      style={{
        backgroundColor: theme === "light" ? "#fff" : "#333",
        color: theme === "light" ? "#333" : "#fff",
        padding: "1rem",
      }}
    >
      <h1>내 웹사이트</h1>
      <button onClick={toggleTheme}>
        {theme === "light" ? "다크 모드" : "라이트 모드"}
      </button>
    </header>
  );
}

 

 

src/components/Content.tsx 

import { useContext } from "react";
import { ThemeContext } from "../contexts/ThemeContext";



export default function Content() {
  const themeContext = useContext(ThemeContext);
  if (!themeContext) throw new Error("ThemeContext not found");

  const { theme } = themeContext;

  return (
    <main
      style={{
        backgroundColor: theme === "light" ? "#f5f5f5" : "#222",
        color: theme === "light" ? "#333" : "#fff",
        padding: "1rem",
      }}
    >
      <p>현재 테마: {theme}</p>
      
    </main>
  );
}

 

src/pages/ThemeContext.tsx

import { createContext, useContext, useState, ReactNode } from "react";

type Theme = "light" | "dark";

type ThemeContextType = {
  theme: Theme;
  toggleTheme: () => void;
};

export const ThemeContext = createContext<ThemeContextType | undefined>(
  undefined
);

type ThemeProviderProps = {
  children: ReactNode;
};

export default function ThemeProvider({ children }: ThemeProviderProps) {
  const [theme, setTheme] = useState<Theme>("light");

  const toggleTheme = () => {
    setTheme(theme === "light" ? "dark" : "light");
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

 

 

src/App.tsx

import React from "react";
import logo from "./logo.svg";
import "./App.css";
import "@fontsource/material-icons";
import ThemeTest from "./pages/ThemeTest";


function App() {
  return <ThemeTest />;
}

export default App;

 

 

 

2. 언어 변경

더보기

src/contexts/ThemeContext.tsx

import { useContext } from "react";
import { ThemeContext } from "../contexts/ThemeContext";
import { LanguageContext } from "../contexts/LanguageContext";

export default function Content() {
  const context = useContext(ThemeContext);
  if (!context) throw new Error("ThemeContext not found");

  const { theme } = context;

  const languageContext = useContext(LanguageContext);
  if (!languageContext) throw new Error("LanguageContext not found");

  const { language } = languageContext;

  return (
    <main
      style={{
        backgroundColor: theme === "light" ? "#f5f5f5" : "#222",
        color: theme === "light" ? "#333" : "#fff",
        padding: "1rem",
      }}
    >
      <p>현재 테마: {theme}</p>
      <p>현재 언어: {language}</p>
    </main>
  );
}


src/components/Header.tsx

// Header, Content, Footer 컴포넌트에서 각각 다른 텍스트 표시
// 언어 변경 버튼 구현

import { useContext } from "react";
import { ThemeContext } from "../contexts/ThemeContext";
import { LanguageContext } from "../contexts/LanguageContext";

export default function Header() {
  const themeContext = useContext(ThemeContext);
  if (!themeContext) throw new Error("ThemeContext not found");

  const { theme, toggleTheme } = themeContext;

  const languageContext = useContext(LanguageContext);
  if (!languageContext) throw new Error("languageContext not found");

  const { language, setLanguage } = languageContext;

  return (
    <header
      style={{
        backgroundColor: theme === "light" ? "#fff" : "#333",
        color: theme === "light" ? "#333" : "#fff",
        padding: "1rem",
      }}
    >
      <h1>내 웹사이트</h1>
      <button onClick={toggleTheme}>
        {theme === "light" ? "다크 모드" : "라이트 모드"}
      </button>
      <button onClick={() => setLanguage(language === "ko" ? "en" : "ko")}>
        {language === "ko" ? "영어" : "한국어"}
      </button>
    </header>
  );
}


src/components/Content.tsx

import { useContext } from "react";
import { ThemeContext } from "../contexts/ThemeContext";
import { LanguageContext } from "../contexts/LanguageContext";

export default function Content() {
  const context = useContext(ThemeContext);
  if (!context) throw new Error("ThemeContext not found");

  const { theme } = context;

  const languageContext = useContext(LanguageContext);
  if (!languageContext) throw new Error("LanguageContext not found");

  const { language } = languageContext;

  return (
    <main
      style={{
        backgroundColor: theme === "light" ? "#f5f5f5" : "#222",
        color: theme === "light" ? "#333" : "#fff",
        padding: "1rem",
      }}
    >
      <p>현재 테마: {theme}</p>
      <p>현재 언어: {language}</p>
    </main>
  );
}

 

(※ src/pages 단에서는 뭔가 따로 추가할 것이나 수정할 것이 없음.)

 

 

 

 

 

 

#17, #18 에서 실습했던 코드는 아래 링크에서 확인 가능.

https://codesandbox.io/p/sandbox/useref-usecontext-practice-1-forked-8gzxkp

 

 

 

 

 

 

반응형