Front-End/React

React] Hook : useContext

왕가👍 2023. 6. 23. 16:40
728x90
반응형

useContext

개발하다 보면 props를 여러 번 넘길 때가 찾아오곤 합니다.

증조부모 → 조부모 → 부모 → 자식 → ...

이렇게 props를 계속 넘기다 보면 엄청난 노가다를 해야 될 수도 있고, 유지보수 측면, 가독성 등 안 좋은 점이 많습니다.

 

이를 개선하기 위해 전역적으로 데이터를 관리할 수 있는 Context를 사용하면 됩니다.

Context를 사용하면 증조부모에서 자식까지 한 번에 데이터를 전달할 수 있습니다.

 

(사용하기에 다른 hook보다 복잡할 수 있습니다.)

 

  • createContext(initialValue)
    • context객체 생성
    • createContext호출 시, Provider와 Consumer 반환
  • Context.Provider
    • 생성한 context를 하위 컴포넌트에 전달
  • useContext(Context);
    • context의 상태 감시
    • 상태값을 가져옴

 

 


 

 

 

useContext 검색해 보면 많이 나오는 마트 관련 소스입니다.

(누가 먼저 작성했는지는 모르겠는데.. 강의에서 봤던 거 같기도 하고..)

 

구조자체는 간단합니다.

최상단 컴포넌트 App에서 최하단 컴포넌트인 Emart3F까지 데이터를 전달하는데, useContext를 사용하지 않으면

App → Mart → Emart → Emart3F 까지 계속 props를 넘겨야 하는 일이 발생합니다.

이를 개선하여 App → Emart3F까지 한 번에 가보도록 수정해 봤습니다.

 

 


 

 

AppContext.js

import { createContext } from "react";

const initialItems = {
  drink: [
    { name: "Powerade", price: "1800" },
    { name: "Coca Cola", price: "1100" }
  ],
  fruits: [
    { name: "Apple", price: "3500" },
    { name: "Grape", price: "6000" }
  ]
};

export const AppContext = createContext(initialItems);

 

Context파일을 따로 분리하여 작성하는 게 가독성이나 관리 측면에서 좋아 보입니다.

객체 안에 drink와 fruites 상품을 초기값으로 지정해 주고, AppContext를 선언하여 다른 컴포넌트에서 사용할 수 있게 export 해줍니다.

 

App.js

// App.js

import React, { useState, useContext } from "react";
import { AppContext } from "./AppContext";
import Mart from "./Mart";

function App() {
  const mart = useContext(AppContext);
  const [items, setItem] = useState(mart);

  const addDrink = (newDrink) => {
    setItem({ ...items, drink: [...items.drink, newDrink] });
    console.log(newDrink.name + "가 추가되었습니다.");
  };

  return (
    <div style={{ position: "absolute", top: "5%", left: "5%" }}>
      <AppContext.Provider value={{ items, addDrink }}>
        <Mart />
      </AppContext.Provider>
    </div>
  );
}
export default App;

 

createContext()를 사용해 AppContext를 선언해 주고, 태그 리턴해줄 때 <AppContext.Provider>로 감싸 주면 됩니다.

그리고 props로 넘길 것들을 정의해 주고 추가해 주면 됩니다. (해당 소스에서는 items, addDrink가 되겠다.)

 

Mart와 Emart 컴포넌트는 그냥 트리를 잡기 위한 컴포넌트로 사용했습니다.

 

Emart3F

// ./components/Emart3F.jsx

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

function Emart3F() {
  const newDrink = { name: "HOT6", price: "1500" };
  const { items, addDrink } = useContext(AppContext);

  return (
    <div>
      <h3>이마트 3층</h3>
      <div>
        음료수
        {items.drink.map((item, index) => {
          return (
            <div key={index}>
              {item.name} : {item.price}원
            </div>
          );
        })}
        <button onClick={() => addDrink(newDrink)}>음료 품목 추가</button>
      </div>

      <div>
        과일
        {items.fruits.map((item, index) => {
          return (
            <div key={index}>
              {item.name} : {item.price}원
            </div>
          );
        })}
      </div>
    </div>
  );
}

export default Emart3F;

 

App.js에서 AppContext.Provider로 감싸줬기 때문에 Emart3F컴포넌트에서 AppContext를 받아서 사용할 수 있습니다.

useContext에 AppContext를 넣어 AppContext에 있는 items랑 addDrink를 구조분해할당하여 받을 수 있고,

받은 것들을 통해 화면에 그려주거나 버튼을 눌러 addDrink함수를 실행시켜 newDrink인 HOT6를 추가할 수 있게 구성되어 있습니다.

 

 


 

 

단점

ContextAPI를 사용하면 규모가 작을 때는 편리하게 개발할 수 있을 것입니다. 하지만, Context값이 변경될 때마다

useContext가 렌더링을 하게 되고, Context.Provider로 감싼 컴포넌트는 모두 리렌더링이 되는 것을 볼 수 있을 것입니다.

따라서 오히려 성능저하를 일으킬 수 있다는 단점이 있습니다.

(해결법은 다음 게시글에서...)

 

 


 

 

 useContext를 사용해서 props 넘기는 데 있어 한층 더 간편하게 개발할 수 있을 것입니다.
간편하게 사용할 수 있는 만큼 성능 저하 측면을 고려하면서 사용해야 될 것이고..
728x90

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

React] Fragment <></>  (0) 2023.06.26
React] Hook : useContext 단점 해결  (0) 2023.06.26
React] Hook : useState 비동기 → 동기 하는 법  (0) 2023.06.22
React] Hook : useReducer  (0) 2023.06.22
React] Hook : useCallback  (0) 2023.06.22