React

[새싹 프론트엔드] 8주차 - 3 Context

ddorongg 2022. 12. 7. 18:41

Context

• 어플리케이션에서 전반적으로 사용할 값을 저장 및 관리

• 예) 사용자의 언어, 로그인 상태, UI 테마 등 환경 설정

 

• 주의사항

• Context와 컴포넌트가 연동되면 컴포넌트를 재사용하기 어려움

• 자주 변경되는 상태인 경우, 사용하지 않는 것이 좋음

   👉 Context 내부의 값이 변경되면 Context를 사용하는 모든 자식 컴포넌트들이 리렌더링 됨

 

일반적인 전역 상태 관리 흐름

컴포넌트 여기저기서 필요한 데이터가 있는 경우

• 주로 최상위 컴포넌트인 App의 state에 넣어서 관리

전달하면서 값을 넘겨줘야 함 (App에서 G로 한 방에 보내줄 방법이 없음)

 

단점 : 유지보수가 어려워짐

 

Props Drilling

 

 

실습) Props Drilling

 

App.js

function App() {
  return <GrandParent value="Hello World!" />;
}

function GrandParent({ value }) {
  return <Parent value={value} />;
}

function Parent({ value }) {
  return <Child value={value} />;
}

function Child({ value }) {
  return <Message value={value} />;
}

function Message({ value }) {
  return <div>전달받은 데이터 : {value}</div>;
}

export default App;

 

실행 결과

 

 

Context를 사용한 전역 상태 관리 흐름

컴포넌트 여기저기서 필요한 데이터가 있는 경우

Context를 생성하여 한번에 원하는 값을 전달 받음 (트리구조와는 별개)

A라는 거를 Context에 저장


Context 사용 방법

Context 객체 생성

• App.js

import { createContext } from 'react';

// MyContext 객체 생성
const MyContext = createContext();

 

Context 객체 내 Provider 컴포넌트를 통한 데이터 전달

Provider 컴포넌트의 하위 컴포넌트는 Context의 데이터에 접근 가능

(Provider는 내부적으로 자식을 가질 수 있음)

 

function App() {
  return (
     <MyContext.Provider value="Hello World!">
         <GrandParent value="Hello World!"/>
     
  );
}

 

Context 객체 내 Provider 컴포넌트는 그냥 들어있는 거니까 갖다 써 왜 들었는지는 신경쓰지말기

 

※ 프로젝트 폴더 안 context폴더 따로 하나 만들어서 그 안에다 파일 생성

컴포넌트, 리듀서도 마찬가지

 

grandparent가 직접 그 값을 가지게 된 것 (my context)

 

provider 쓸 애들만 지붕 씌워주기 - 안 쓸 애들까지 모조리 리렌더링 됨

<P~~>

 <자식>

<P~~>

 

Context 사용 방법

 

App.js

 

실행 결과

 


실습) Context 파일 생성

 

Context.js

import { createContext } from "react";

export const MyContext = createContext("");

 

ParentComponent.js

import React from "react";
import { MyContext } from "./Context";

const ParentComponent = () => {
  return (
    <MyContext.Provider value="안녕하세요">
      <ChildComponent />
    </MyContext.Provider>
  );
};

export default ParentComponent;

 

ChildComponent.js

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

const ChildComponent = () => {
  const value = useContext(MyContext);
  return <div>전달받은 데이터 : {value}</div>;
};

export default ChildComponent;

 

실행 결과

 

Context 기본값 지정

createContext() 함수에 기본값 지정

• 자식 컴포넌트에서 useContext() 함수를 사용하고 있는데, 부모 컴포넌트가 Provider를 사용하지 않은 경우

• value 값을 지정해주지 않았기 때문에 해당 값이 출력될 자리에 아무것도 나타나지 않음

   

👉 기본값을 설정하면 이러한 문제를 방지할 수 있음

 

기본 값 지정 방법

const MyContext = createContext("Default Value");

매개변수로 초기값 넣기

 

 

실습) Context 기본값 지정

 

Context.js

import { createContext } from "react";

//재사용하기 위해 뺴주는 거
export const MyContext = createContext("기본값 지정");

 

ParentComponent.js

import React from "react";
import { MyContext } from "./Context";
import ChildComponent from "./ChildComponent";

const ParentComponent = () => {
  return (
    //<MyContext.Provider value="안녕하세요">
      <ChildComponent />
    //</MyContext.Provider>
  );
};

export default ParentComponent;

 

실행결과

 


테마 변경

실습) 어플리케이션 전체 배경색 변경

 

ThemeContext.js

import { createContext } from "react";

export const ThemeContext = createContext();

 

App.js

function App() {
	const [darkMode, setDarkMode] = useState(false);
	
    return (
	   <ThemeContext.Provider value={{ darkMode, setDarkMode }}>
		  <HomeComponent />
	   </ThemeContext.Provider>
	);
}

 

HomeComponent.js - 전체페이지 총괄(3단으로 쪼갬) 통째로 지붕 씌움

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

import HeaderComponent from "./HeaderComponent";
import MainComponent from "./MainComponent";
import FooterComponent from "./FooterComponent";
import "./HomeComponent.scss";

const HomeComponent = () => {
  const data = useContext(ThemeContext);
  console.log(data);

  return (
    <div className="container">
      <HeaderComponent />
      <MainComponent />
      <FooterComponent />
    </div>
  );
};
export default HomeComponent;

 

HeaderComponent.js

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

const HeaderComponent = () => {
  const { darkMode, setDarkMode } = useContext(ThemeContext);
  //dark모드는 False로 나옴

//dark모드를 True로 만듬
  const toggleDarkMode = () => {
    setDarkMode(!darkMode);
  };

  const theme = {
    backgroundColor: darkMode ? "black" : "white",
    color: darkMode ? "white" : "black",
  };

  return (
    <div className="header" style={theme}>
      헤더
      {
      darkMode ? (
        <button className="toggleBtn" onClick={toggleDarkMode}>
          😴
        </button>
      ) : (
        <button className="toggleBtn" onClick={toggleDarkMode}>
          😵
        </button>
      )}
    </div>
  );
};

export default HeaderComponent;

 

MainComponent.js

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

const MainComponent = () => {
  const { darkMode } = useContext(ThemeContext);

  const theme = {
    backgroundColor: darkMode ? "black" : "white",
    color: darkMode ? "white" : "black",
  };

  return (
    <div className="main" style={theme}>
      메인
    </div>
  );
};

export default MainComponent;

 

FooterComponent.js

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

const FooterComponent = () => {
  const { darkMode } = useContext(ThemeContext);

  const theme = {
    backgroundColor: darkMode ? "black" : "white",
    color: darkMode ? "white" : "black",
  };

  return (
    <div className="footer" style={theme}>
      푸터
    </div>
  );
};

export default FooterComponent;

 

HomeComponent.scss

* {
  margin: 0;
}

.container {
  width: 100%;
  height: 100vh;
  display: flex;
  flex-direction: column;
}

@mixin flexCenter() {
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 30px;
}

.header {
  @include flexCenter();
  width: 100%;
  height: 100px;
  border-bottom: 3px solid gray;
}

.main {
  @include flexCenter();
  flex: 1;
}

.footer {
  @include flexCenter();
  width: 100%;
  height: 100px;
  border-top: 3px solid gray;
}

.toggleBtn {
  background-color: transparent;
  border: none;
  font-size: 30px;
}

 

실행결과

 

예제 오류

 

1. ThemeContext 계속 찾지 못하는 상황 발생  styledcomponent라고 되어 있는 거

내 경로로(import { ThemeContext } from "./ThemeContext";) 재설정

 

2. export default HomeComponent; 안 해줘서 추가

 

3.scss 연결 안 됨 - 확장프로그램 설치 - className js마다 추가

 

도와주신 앞자리 천사님들 감사합니다

 


 

[실습회고]

인풋 값을 받아서 blue라고 입력하면 그대로 body backgroundColor를 바꾸는 문제였는데 풀다가 중간에 그 인풋 값을 어떻게 반영해야 할 지 길을 잃어서 수련이 부족하다고 생각 ,,, 풀이를 보니 너무 어렵게 접근한 것 같고 ,,,. 그래도 기 죽지 말자 처음 배우는건데 어떻게 첨부터 hook을 자유자재로 다루겠어!! 그냥 못하는 것을 받아들이고 노력하면 될 것.

 

풀이

const ColorBox = () => {
  const value = useContext(ColorContext);

  const boxStyle = {
    background: value,
  };

  return <div style={boxStyle}></div>;
};

 

내 코드

 const ColorBox = () => {
//   const { changeBg, setchangeBg } = useContext(Color);
//   const bgColor = changeBg;

//   useEffect(() => {
//     const inputColor = prompt("바꿀 배경 색상을 입력하세요");

//     setchangeBg(inputColor);
//   }, []);

일단 저 프롬프트는 다른 컴포넌트에 들어가 있어야 하고 바꾸고자 한 것은 변수에 담았어야 한다

아무튼 요상하게 코드 짜다 실패했으니 다시 개념을 다잡기

 

 

 

 

 

새싹DT 기업연계형 프론트엔드 실무 프로젝트 과정 8주차 블로그 포스팅