[React Hooks 총정리] useContext + Context API

혜빈·2024년 7월 5일
0

REACT 보충개념

목록 보기
31/48

useContext

  • context로 받아온 데이터를 쉽게 받아볼 수 있게 도와주는 역할을 함

  • 리액트로 만든 앱은 여러 개의 컴포넌트 들로 이루어져 있음

  • 최상위 앱에서 시작해서 트리형태로 뻗어나감

  • 부모 컴포넌트 -> 자식 컴포넌트로 Props를 통해 전달됨

// App

<Header user={user} />

// Header

<SearchBar user={user} />
  • 이렇게 부모컴포넌트가 자식컴포넌트의 태그에 하나하나 props를 넣어서 단계별로 전달을 해줘야함

  • 앱 내부에서 많은 컴포넌트들이 공통적으로 필요한 전역적인 데이터가 존재할 수 있음(Global Data)

  • 현재 로그인된 사용자 정보, 테마, 언어 등

  • 전역적인 데이터를 단계별로 전달해야한다면 비효율적임 -> Context API 사용

  • Context는 앱 안에서 전역적으로 사용되는 데이터들을 여러 컴포넌트들 끼리 쉽게 공유할 수 있는 방법을 제공해줌

  • 해당 데이터를 갖고 있는 상위 컴포넌트가 "데이터 필요한 사람!"하고 하위 컴포넌트들에게 방송을 해주면 하위 컴포넌트들은 트리 어디에 위치해있든 상관없이 "나 그 데이터 쓸래"라고만 해주면 해당 값에 접근 가능

  • 사용자 정보, 테마, 앱에서 사용된 언어와 같은 수많은 컴포넌트들이 필요한 전역적 데이터를 전달하기에 굉장히 편리함


Props Drilling

  • 만약 최하위 컴포넌트인 C와 E에 필요한 데이터를 전달하려면 Props를 통해 모든 중간 컴포넌트들을 거쳐서 단계별로 전달해야 함

  • 문제점: 가장 아래의 C와 E 컴포넌트만 데이터가 필요함에도 불구하고 데이터는 전달과정에서 모든 컴포넌트들을 거쳐가야 함
    -> 컴포넌트들이 받는 props 많아짐, 코드 길어짐, 오류 발생시 부모 컴포넌트들을 다 확인해봐야 함


Context는 꼭 필요할때만 하자!

  • Context를 사용하면 컴포넌트를 재사용하기 어려워 질 수 있음

  • Context의 주요 목적은 다양한 레벨의 많은 컴포넌트들에게 전역적인 데이터를 전달하기 위함임

  • props drillig을 피하기 위한 목적이라면 Component Composition(컴포넌트 합성)을 먼저 고려해보자


Component Composition(컴포넌트 합성)이란?

  • 여러 개의 컴포넌트를 조합하여 새로운 컴포넌트를 만드는 방법
  • 레고 블록을 조립하는 것으로 생각하기
  • props drilling 문제를 해결하고 코드의 재사용성과 유연성을 높일 수 있음
function Card({ children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

function CardTitle({ title }) {
  return <h2>{title}</h2>;
}

function CardContent({ content }) {
  return <p>{content}</p>;
}

// 이제 이 컴포넌트들을 합성해봅시다
function MyCard() {
  return (
    <Card>
      <CardTitle title="안녕하세요!" />
      <CardContent content="이것은 카드 내용입니다." />
    </Card>
  );
}
  • 여기에서 Card는 부모 컴포넌트이고, CardContent는 자식 컴포넌트임

  • 이들을 MyCard에서 조합해서 하나의 완성된 카드를 만들었음

  • CardTitle과 CardContent를 다른 곳에서도 쉽게 사용 가능 -> 재사용성

  • 카드의 내용을 쉽게 변경 가능 -> 유연성

  • 장점

  1. 코드의 재사용성 증가
  2. 관심사의 분리
  3. Props drilling 감소
  4. 유연한 컴포넌트 구조

children prop이란?

  • 컴포넌트 태그 사이에 넣은 모든 것이 children으로 전달됨
<Card>
  <CardTitle title="제목" />
  <CardContent content="내용" />
</Card>
  • 여기에서 < CardTitle >과 < CardContent >가 Card 컴포넌트의 Childern임

  • 즉, 빵, 치즈, 햄이 있다고 하면 빵(Card 컴포넌트), 치즈(CardTitle 컴포넌트), 햄(CardContent)이 됨

  • 이 재료(컴포넌트)를 조합해서 완성된 샌드위치가 MyCard임

  • 장점

  1. 코드 재사용 -> 같은 컴포넌트를 여러 곳에서 사용 가능
  2. 유지보수 -> 각 부분 독립적으로 수정 가능
  3. 가독성 -> 큰 컴포넌트를 작은 조각으로 나누어 이해하기 쉬움

실제 코드 구현

기본 코드 구현

UseContext.js

Page.jsx

Content.jsx

Footer.jsx

Header.jsx

Page.jsx

Context 사용해서 웹 사이트 개선하기

  • UseContext.js의 isDark를 보면 앱의 전체적인 테마에 대한 정보를 담고 있기 때문에 전역적 데이터라고 볼 수 있음

  • 앱 규모가 크다면 수많은 컴포넌트들이 isDark를 필요로 할 것임

  • 플로우를 보면 UseContext 컴포넌트가 Page 컴포넌트에게 isDark를 props로 넘겨줌 -> Page 컴포넌트는 isDark를 받아오지만 실제적으로 사용하고 있지 않음(단지 자식 컴포넌트에게 전달하기 위해 받아옴) = isDark를 필요로 하지 않는 중간 컴포넌트임 -> 비효율적임

  • Context를 사용해서 중간 컴포넌트없이 데이터가 필요한 컴포넌트들에게 공유해보자

  1. Context 폴더 생성
  2. ThemeContext.js 파일 생성

  1. UseContext.js에서 ThemeContext import 하기

  2. Page 컴포넌트를 ThemeContext.Provider로 감싸주기

  • value라는 prop을 하나 받고, value안에 우리가 전달하고자 하는 데이터를 넣어주기

  • 이렇게 하면 ThemeContext.Provider가 감싸는 모든 하위 컴포넌트는 value로 넣어준 isDark, setIsDark에 props없이 접근 가능함
  1. 정확한 확인을 위해서 Page 컴포넌트가 가지고 있는 모든 프로세스 삭제해주기

  1. Page.jsx에 필요없는 코드 삭제하기

  1. Header.jsx, Content.jsx, Footer.jsx에서 필요없는 코드 삭제하고 useContext import 해주기

Header.jsx

Content.jsx

Footer.jsx

최종 화면

  • 잘 작동함

총정리

  1. 상위 컴포넌트에서 컨텍스트를 불러와서 모든 하위 컴포넌트들에게 "필요한 사람?"하고 방송함

  1. Page 컴포넌트는 필요 없으니까 정보를 받아오지 않음

  1. Page 컴포넌트의 하위 컴포넌트인 Header 컴포넌트, Content 컴포넌트, Footer 컴포넌트는 useContext를 사용해서 "저 필요합니다!"하고 반응함

  1. 컴포넌트 내부에서 정보를 사용해서 다크모드 구현 성공 ~!

ThemeContext.js에서 초기값에 대해 알아보기

  • 만약 useContext로 ThemeContext에 대한 정보를 받아왔는데 상위에서 Context의 Provider로 감싸주지 않았다면(value가 없는 상태라면) ThemeContext에서 인자로 넘겨준 초기값을 받아오게 됨

  • 방금 만듬 앱은 value를 사용해서 값을 넘겨주기 때문에 초기값이 필요하지 않음


실제 코드 구현 2

  1. UserContext 생성 - 사용자에 대한 정보를 넣을 컨텍스트

  1. 상위 컴포넌트에 import해주기(UseContext.js)

  2. 가장 상위에 Provider로 감싸주기

  • 전달해줄 value로 사용자 넣어주기

  1. Header에서 사용자 받아오기

  1. Content도 변경해주기

profile
최강 개발자를 꿈꾸는 병아리

0개의 댓글