[React] 다크 모드 구현하기(feat. useContext)

kimi·2023년 8월 11일
2

저번에는 https://velog.io/@kiminn/React-useContext란 를 통해서 useContext에 대해서 알아봤습니다

이제 useContext를 사용하여 사용자 입장에서 너무나도 당연해져버린 웹 페이지의 다크 모드 테마를 구현해보겠습니다


App.js(상태) > Page.jsx(X) > Header & Content & Footer(상태)

// App.js
import { useState } from 'react';
import './App.css';
import Page from './components/Page';

function App() {
    const [isDark, setIsDark] = useState(false); // 기본적으로 isDark가 꺼져있음(light mode)

    return <Page isDark={isDark} setIsDark={setIsDark}/>;
}

export default App;

아래 코드는 App.js에서 만들어진 상태(isDark)를 필요가 없는 중간 컴포넌트인 Page.jsx가 전달받은 모습입니다 => 그럴 필요가 있나? => (X)

// Page.jsx
import Content from './Content';
import Footer from './Footer';
import Header from './Header';

const Page = ({isDark, setIsDark}) => {
    return (
        <div className="page">
            <Header isDark={isDark} />
            <Content isDark={isDark} />
            <Footer isDark={isDark} setIsDark={setIsDark} />
        </div>
    );
};

export default Page;

앱의 규모가 커지면서 이러한 중간 컴포넌트들이 만약 수십 개가 된다면, 일일이 찾아다니면서 디버깅하고 유지보수하는 과정이 아주 끔찍해질 것입니다


먼저 isDark는 테마에 대한 정보를 담고있는 것으로 전역적인 데이터 관리가 필요합니다
=> useContext 사용하여 관리

1. createContext를 사용하여 context객체를 생성 후 export !!

🌱 createContext : Context 객체 생성

// ThemeContext.js
import { createContext } from "react";
export const ThemeContext = createContext(null);

2. Provider를 부모 컴포넌트(App.js)에 작성

🌱 Provider : 생성한 context(상태)를 자식 컴포넌트에 공유

import { useState } from 'react';
import './App.css';
import Page from './components/Page';
import { ThemeContext } from './context/ThemeContext';
function App() {
    const [isDark, setIsDark] = useState(false); // lightmode
    return (
            <ThemeContext.Provider value={{ isDark, setIsDark }}>
                {/* value 안에는 전달하고자하는 데이터를 집어넣는다
             	모든 하위컴포넌트에 전달해야하는 isDark, setIsDark 넣어주기
          		props를 사용하지않고 접근할 수 있다 */}
                <Page />
            </ThemeContext.Provider>
    );
}
export default App;

3. 하위 컴포넌트(Header, Context, Footer)에 공유

// Header.jsx
import React, { useContext } from 'react';
import { ThemeContext } from '../context/ThemeContext';

// props로 전달됐던 ()안의 isDark 삭제
const Header = () => {
    const { isDark } = useContext(ThemeContext);
  
    return (
        <header
            className="header"
            style={{
                backgroundColor: isDark ? 'black' : '#ff7cc8',
                color: isDark ? 'white' : '#2d2d2d',
            }}
        >
            <h1>hi, user</h1>
        </header>
    );
};

export default Header;
// Context.jsx
import React, { useContext } from 'react';
import { ThemeContext } from '../context/ThemeContext';

// props로 전달됐던 ()안의 isDark 삭제
const Content = () => {
    const { isDark } = useContext(ThemeContext);

    return (
        <div
            className="content"
            style={{
                backgroundColor: isDark ? '#333333' : '#ffebeb',
                color: isDark ? '#ffffff' : '#ff0095',
            }}
        >
            <p>user, Click the button below ↘️</p>
        </div>
    );
};

export default Content;
// Footer.jsx
import React, { useContext } from "react";
import { ThemeContext } from "../context/ThemeContext";

// props로 전달됐던 ()안의 isDark, setIsDarkt삭제
const Footer = () => {
    const {isDark, setIsDark} = useContext(ThemeContext);
    const toggleTheme = () => {
        setIsDark(!isDark);
    };

    return (
        <footer
            className="footer"
            style={{
                backgroundColor: isDark ? 'black' : '#ff7cc8',
                color: isDark ? 'white' : '#333333',
            }}
        >
            <button className="button" onClick={toggleTheme}>
                Dark Mode
            </button>
        </footer>
    );
};

export default Footer;

4. 중간 컴포넌트(Page.jsx)에서 더 이상 필요하지 않은 상태의 값(isDark) 제거

// Page.jsx
import Content from './Content';
import Footer from './Footer';
import Header from './Header';

const Page = () => {
    return (
        <div className="page">
            <Header />
            <Content />
            <Footer />
        </div>
    );
};

export default Page;

그러면 이렇게 하단의 버튼을 눌렀을 때 다크모드로 전환이 되는 페이지가 구현됩니다


같은 방법으로 user의 이름도 관리할 수 있습니다

// UserContext.js
import { createContext } from "react";
export const UserContext = createContext(null); 
// App.js
import { useState } from 'react';
import './App.css';
import Page from './components/Page';
import { ThemeContext } from './context/ThemeContext';
import { UserContext } from './context/UserContext';

function App() {
    const [isDark, setIsDark] = useState(false); 

    return (
        <UserContext.Provider value={'kimi'}> // userContext
            <ThemeContext.Provider value={{ isDark, setIsDark }}>
                <Page />
            </ThemeContext.Provider>
        </UserContext.Provider>
    );
}

export default App;
// Header.jsx
import React, { useContext } from 'react';
import { ThemeContext } from '../context/ThemeContext';
import { UserContext } from '../context/UserContext';

const Header = () => {
    const { isDark } = useContext(ThemeContext);
    const user = useContext(UserContext);
    
    return (
        <header
            className="header"
            style={{
                backgroundColor: isDark ? 'black' : '#ff7cc8',
                color: isDark ? 'white' : '#2d2d2d',
            }}
        >
            <h1>hi, {user}</h1>
        </header>
    );
};

export default Header;

위와 같은 방법으로 Content도 수정하면 user(사용자 이름)부분도 useContext를 사용하여 관리할 수 있습니다




별 코딩님의 유튜브 강의
https://www.youtube.com/watch?v=LwvXVEHS638
참고한 사이트
https://jaylee-log.tistory.com/55

profile
no namae wa

2개의 댓글

comment-user-thumbnail
2023년 8월 16일

잘보고갑니다~

1개의 답글