본문 바로가기
Front-End/React.js

ContextAPI

by kk님 2023. 3. 13.

Props Drilling
정말 정말 정말 해결하고 싶은 문제.
사용하지 않는 컴포넌트에서, 단지 전달해주기 위해 props를 다 적어줘야 하는데, 이를 피하기 위해 state를 전역적으로 관리를 하는 redux, react-query등이 있고 props drilling을 회피하기 위해 ContextAPI를 사용해 볼 수도 있다.

 

 ContextAPI는 전역적으로 관리하기 보다는 목적이 props drilling 피하기 위한 것. ContextAPI는 전달만 해주더라도 랜더링이 일어나기 때문에 랜더링 최적화를 위해서는 redux등을 이용한다고 한다.
 
 그리고 redux, react-query 등의 패키지를 사용하기 위해서는 설치해야 하고.. 또 아직 규모가 작은 프로젝트에서 활용할 것이기 때문에 일단 ContextAPI에 대해 익숙해지려고 한다.
 
 기본적인 내용이지만, 익숙하지 않으면 어려워보이기 때문에 예제를 다 본 다음, 한줄씩 떠올리면서 코드를 작성했다.
작성하면서 들었던 궁금증 목록
1. createContext, useContext는 각각 어떤 컴포넌트에서 호출되는 걸까?

 (1) cerateContext는 따로 모듈화가 가능한 글을 읽었다. 그러면 Provider를 생성하는 건 <App/> 컴포넌트일까?

 - 일단 Context를 모듈화 한다.

 - Provider를 사용할 컴포넌트에 Context를 import해서 사용한다.

 - useComsumer를 사용할 컴포넌트에 import Context해서 Context를 불러와 props를 사용하면 된다. 

//src/context/MyContext

import { createContext } from "react";

export const MyContext = createContext(null);

.

//src/App

import { MyContext } from "./context/MyContext";
import Information from "./Information";

export default function App() {
  return (
    <div>
      <MyContext.Provider value={{ info: "hello-kk" }}>
        <Information />
      </MyContext.Provider>
    </div>
  );
}

.

//src/Information

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

function Information() {
  const value = useContext(MyContext);
  return <div>{value.info}</div>;
}

export default Information;


2. Provider는 부모 컴포넌트에서 한번만 하면 되는건가?
 - 하위 컴포넌트에서 다른 value를 사용하길 바란다면 Provider 필요
3. createContext에 어떤 인자가 필요하지?
 - 전달할 default props

 (1) 그런데, 여기서도 의아했던 부분이 그러면 왜 MyContext.Provider에서 value 값을 채워야 하는걸까?

 - useContext할 때 Provider가 상위에 없다면 default value를 사용한다고 한다. 와 이해하기 어려웠다. 왜냐면, Provider를 무조건 쓴다고 생각했으니까. 하지만 이렇게 모듈화로 따로 분리한다면 확실히 안쓰이게 되지만 useContext를 호출할 수 도 있는 상황이 있을수도 있을 것 같은데 정확한 사례는 찾아보진 못했다.

<MyContext.Provider value={ 객체 }>
...
<MyContext.Provider/>

4. useContext에 어떤 인자가 필요하지?
 - Context(Provider)
5. createContext가 리턴하는 건?
 - Context(Provider)
6. useContext가 리턴하는 건?
 - 객체(props)
7. value를 안 적으면?
 - 꼭 value를 사용해야 한다. default value를 적어주기 때문에 value를 쓰지 않아도 되는게 아닌가? 했는데, default value는 안전장치이기도 하고.. value를 적어주지 않으면 오류가 발생한다.
8. useContext()를 안한다면?

 - class형에서는 <MyContext.Consumer>를 통해 값을 받아오는 예제들을 많이 봤다. 

<MyContext.Consumer>
{state => ... }
</MyContext.Consumer>

9. 꼭 value라면 value로 이름이 같아야 하나?

 - 일반적으로 이렇게 쓰이는것 같은데, return되는건 value안에 든 값이기 때문에 다른 변수명을 사용해도 작동한다.

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

function Information() {
  const a = useContext(MyContext);
  console.log(a);
  return <div>{a.info}</div>;
}

export default Information;

10. createContext랑 useContext랑 같은가?
 - 아님. 매개변수와 리턴이 다르다. (서로 반대 느낌..)
11. Provider로 다음처럼 props를 전달할때, App 컴포넌트 안의 h1은 일단 영향을 받지 않는다. 직접 value 변수명으로 전달했어도 일단 <App/> 컴포넌트에서는 value를 사용할 수가 없다. 

const themeContext = createContext({ color: "red" });

export default function App() {
  const theme = useContext(themeContext);
  return (
    <themeContext.Provider value={{ color: "blue" }}>
      <h1 style={theme}>Hello-kk</h1>
      {/* red? blue? */}
      <Sub1 />
    </themeContext.Provider>
  );
}

12. 전달 value들이 여러개면?

 - 객체로 전달한다.
13. 꼭 value라는 변수명으로 전달 해야 하나?

 - 공식 문서에서는 다음과 같이 설명하고 있다.

Provider 컴포넌트는 value prop을 받아서 이 값을 하위에 있는 컴포넌트에게 전달합니다.

14. 하위 컴포넌트
 (1) Context.Provider로 직접 감싸지지 않은 경우(여기선 모듈화를 한 경우를 의미한다.)
    => useContext로 value를 그대로 사용할 수 있다.

const value = useContext(themeContext);

 (만약 themeContext가 모듈화되어 export 가능한가? 아니면 어떻게 ..? )
예제에서는, createContext(default value)으로 할당했는데,
useContext()를 createContext()를 하면 value 없이 Provider를 생성할 수 있고, 다음처럼 props를 전달할 수 있다. 하지만 모듈화를 한다면 default value를 꼭 적어주어야 한다는 오류 메시지를 볼 수 있다.

const tmpContext = createContext()
<tmpContext.Provider value={ 객체 }>
<Sub />
<tmpContext.Provider>

15. 사용할 곳에서 StoreContext를 import 한 뒤, <StoreContext.Consumer> 또는 useContext(StoreContext)

- Consumer보다 useContext()가 직관적이고, Consumer는 class형 컴포넌트, useContext는 함수형 컴포넌트에서 훅으로 주로 사용하는 예제들을 많이 봤다.

import StoreContext from "경로";
<StoreContext.Comsumer>
	{store => store.속성 }
</StoreContext.Comsumer>

useContext의 경우 context.Consumer 없이도 컨텍스트를 활용 가능.
단순하게

import StoreContext from "경로";

const store = useContext(StoreContext)
	{store => store.속성 }

16. 여기서 궁금한건, 기본 값이 든 context를 생성했는데, value를 왜 또 지정해줘야 하는 걸까? 사용할수가 없으면 왜 default 값을 줄 수 있는거지?

 - 3번 에 기록해두었다.

  const dfuContext = createContext({ color: "yellow" });
  
  return (
    <div>
      <dfuContext.Provider>
        <Sub3 />
      </dfuContext.Provider>
    </div>
  );

https://beta.reactjs.org/reference/react/createContext

 

createContext

A JavaScript library for building user interfaces

beta.reactjs.org

검색: create context default value
defaultValue는 오직(!) 컴포넌트가 트리 상에서 어떠한 Provider와도 매칭되지 않을때 사용된다.
구성 요소를 래핑하지 않고 격리된 상태에서 테스트하는 데 유용할 수 있습니다.
=> 3번에 적어놓음.
기본적으로는 <defaultContext.Provider value={}>로 전달해야 하는 것 같다

'Front-End > React.js' 카테고리의 다른 글

커스텀 훅  (0) 2023.03.14
프로젝트 디렉토리 구조  (0) 2023.03.13
컴포넌트 모듈화 - Router 컴포넌트 모듈화부터  (1) 2023.03.12
import 자동완성  (0) 2023.03.10
컴포넌트 리렌더링 방지하기! React.memo  (0) 2023.03.06