React

contextAPI와 useContext 사용법 (다크모드 구현) 2 - 더사용쉬운 버전으로 만들기!! (리팩토링)

hihiha2 2023. 3. 17. 16:01

전역적인 상태관리를 위해서 contextAPI와 useContext를 사용하였다.

필요없는 컴포넌트에까지 props drilling을 해줄때보다 가독성측면이나, 효율성, 유지보수 모두 뛰어나다.

 

하지만 필요한 페이지마다 import를 많이 해주는 나름의 단점(?)이 존재한다.

그래서 이것을 조금이라도 줄여보조자하는 방법을 적어보고자 한다.

 

어제 만들었던 다크모드의 리팩토링을 통해 useContext를 다른 방식으로도 사용해보겠다.

 

 

//footer.js

import React, { useContext } from "react";
import { ThemeContext } from "./ThemeContext";

export default function Footer() {
  const { isDark, setIsDark } = useContext(ThemeContext);

footer.js을 예시로 보면,

1️⃣ {useContext}, {ThemeContext}를 각각 일일이 import를 해줘야한다.  ➡️  import 간단하게 바꾸기 

2️⃣ 또 state를 사용할때마다 useContext(ThemeContext)를 적어야한다 ➡️   짧게 바꾸기 

 

또한 버튼을 누르면 모드의 변화가 일어나는 Toggle함수도 footer안에 있는데, 

이 페이지는 Toggle을 여기서 한번만 적어도 돼서 여기에 적어도 괜찮지만

Toggle을 여러군데에서 써야한다면 이것도 ThemeContext.js에서 관리를 해줄수 있다.

3️⃣ onToggle함수를 한번만 만들어서 쓰도록하기 

 

 

 

 

🔫 리팩토링 순서 🔫

📤 ThemeContext.js 📤

1. ThemeContextProvider()을 만든다.

export funtion ThemeContextProvider(){

};

 

2.  props로 하위 컴포넌트에 전달하고 싶은 값들을 선언한다.

💌 전달하고 싶은것 💌 
① isDark
② onToggle

 

export function ThemeContextProvider({ children }) {
  const [isDark, setIsDark] = useState(false);

  const onToggle = () => {
    setIsDark(!isDark);
  };

 

3. return 안에 <ThemeContext.Provider>  </ThemeContext.Provider>  를 만든다.

  return (
    <ThemeContext.Provider>
     
    </ThemeContext.Provider>
  );
}

 

4. 그 안에 props를 적어준다.

 return (
    <ThemeContext.Provider >
      {children}
    </ThemeContext.Provider>
  );
}

 

5.  전달하고 싶은것을 value값 안에 담아준다.  (isDark, onToggle)

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

 

 

6. 2️⃣ 또 state를 사용할때마다 useContext(ThemeContext)를 적어야한다 ➡️   짧게 바꾸기 

위의 문제를 해결하기 위하여 맨 아래에 함수를 하나 선언한다.

export const useDark = () => useContext(ThemeContext);

 

 

ThemeContext.js의 전체코드

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

const ThemeContext = createContext();

export function ThemeContextProvider({ children }) {
  const [isDark, setIsDark] = useState(false);

  const onToggle = () => {
    setIsDark(!isDark);
  };

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

export const useDark = () => useContext(ThemeContext);

 

 

👍 이렇게 하면 이제 전역적인 값들을 하위 컴포넌트에 보낼 준비가 다 됐다!!

어디에서부터 이 값들을 내려보내줄건지를 결정하고, 그 지점에 <ThemeContextProvider>를 넣어주면 된다.

 

☂️ App.js ☂️ 

App.js를 기준으로 ThemeContext를 내려줄 것이기 때문에 여기에 적는다.

 

물론 먼저 상단에 ThemeContextProvider를 import해준다.

 

1. import

import { ThemeContextProvider } from "./ThemeContext";

 

2. <ThemeContextProvider>

<ThemeContextProvider>
<Page />;
</ThemeContextProvider>
import React from "react";
import "./App.css";
import Page from "./Page";
import { ThemeContextProvider } from "./ThemeContext";

export default function App() {
  return (
    <ThemeContextProvider>
      <Page />;
    </ThemeContextProvider>
  );
}

 

 

이제 Page의 하위에는 일일이 prosp를 내려주지 않아도, 콕찝어서 전역값을 전달할 수 있다.

👍 유지보수, 가독성 👍

 

위의 props들을 전달받아야 할 컴포넌트들을 적어보자.

1. Header.js.   ➡️ isDark
2. Content.js.   ➡️ isDakr
3. Footer.js.  ➡️ isDark, onToggle

 

 

 

📩 Header.js 📩

1. import

import { useDark } from "./ThemeContext";

 

Header.js의 상단에

{useContext}와 {ThemeContext}를 둘다 import해주는것 대신, 

만들어준 useDark 하나만 import해주면 된다.

 

 

2.     const { isDark } = useDark();

 

 

const 안에 props로 받고 싶은 값을 적고, useDark()를 적는다.

export default function Header() {
  const { isDark } = useDark();

  return (
    <header
      className="header"
      style={{
        backgroundColor: isDark ? "black" : "lightgrey",
        color: isDark ? "white" : "black",
      }}
    >
      <h1>모드 테스트중!!!</h1>
    </header>
  );
}

 

 

📩  Content.js 📩

받아와야하는 props가 같아서 위의 Header.js와 아예 똑같이 하면 된다.

1. import

   import { useDark } from "./ThemeContext";

  

2.   const { isDark } = useDark();  

  const 안에 props로 받고 싶은 값을 적고, useDark()를 적는다.

import React from "react";
import { useDark } from "./ThemeContext";

export default function Content() {
  const { isDark } = useDark();

  return (
    <div
      className="content"
      style={{
        backgroundColor: isDark ? "black" : "white",
        color: isDark ? "white" : "black",
      }}
    >
      <p>화이팅 화이팅 화이팅 🔥</p>
    </div>
  );
}

 

 

📩  Footer.js 📩

1. import

import { useDark } from "./ThemeContext";

   

2.   const { isDark, onToggle } = useDark();   

Footer에 toggle버튼이 있기때문에 여기에는 onToggle도 넘겨준다.

   

  

3.  <button  onClick={onToggle}> 

 

 버튼에 onClick값으로 onToggle을 넣어준다.

import React from "react";
import { useDark } from "./ThemeContext";

export default function Footer() {
  const { isDark, onToggle } = useDark();

  return (
    <footer
      className="footer"
      style={{
        backgroundColor: isDark ? "black" : "white",
        color: isDark ? "white" : "black",
      }}
    >
      <button className="button" onClick={onToggle}>
        Mode Change
      </button>
    </footer>
  );
}

 

✅ 결과물 ✅ 

 

 

 

 

✅ 전체코드 ✅ 

 

//ThemeContext.js

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

const ThemeContext = createContext();

export function ThemeContextProvider({ children }) {
  const [isDark, setIsDark] = useState(false);

  const onToggle = () => {
    setIsDark(!isDark);
  };

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

export const useDark = () => useContext(ThemeContext);



//App.js

import React from "react";
import "./App.css";
import Page from "./Page";
import { ThemeContextProvider } from "./ThemeContext";

export default function App() {
  return (
    <ThemeContextProvider>
      <Page />;
    </ThemeContextProvider>
  );
}



//Header.js

import React from "react";
import { useDark } from "./ThemeContext";

export default function Header() {
  const { isDark } = useDark();

  return (
    <header
      className="header"
      style={{
        backgroundColor: isDark ? "black" : "lightgrey",
        color: isDark ? "white" : "black",
      }}
    >
      <h1>모드 테스트중!!!</h1>
    </header>
  );
}



//Content.js

import React from "react";
import { useDark } from "./ThemeContext";

export default function Content() {
  const { isDark } = useDark();

  return (
    <div
      className="content"
      style={{
        backgroundColor: isDark ? "black" : "white",
        color: isDark ? "white" : "black",
      }}
    >
      <p>화이팅 화이팅 화이팅 🔥</p>
    </div>
  );
}



//Footer.js

import React from "react";
import { useDark } from "./ThemeContext";

export default function Footer() {
  const { isDark, onToggle } = useDark();

  return (
    <footer
      className="footer"
      style={{
        backgroundColor: isDark ? "black" : "white",
        color: isDark ? "white" : "black",
      }}
    >
      <button className="button" onClick={onToggle}>
        Mode Change
      </button>
    </footer>
  );
}