도리쓰에러쓰

[React] 전역 상태 관리하기 - useContext() 본문

React/React

[React] 전역 상태 관리하기 - useContext()

강도리 2022. 4. 21. 18:58

1. useContext()란?

보통 부모 Component에서 자식 Component로 props를 통해 데이터를 전달하는데, 그 깊이가 깊어진다면 거쳐가야 하는 Component도 많아진다. 그렇게 된다면 코드를 반복적으로 작성해야 하는 일도 많아지고, 변수명이 바뀌면 거쳐가는 모든 Component에서 변수명을 수정해야 하는 등 비효율적인 문제가 발생한다.

 

이런 비효율적인 문제를 해결하는데에 Context가 꽤나 유용하다. Context를 사용하면 전역적으로 데이터를 공유하기 때문에 데이터가 필요한 Component에서 바로 사용이 가능하게 된다. React Hook인 useContext()는 Context를 좀 더 편하게 사용할 수 있게하는데, 우선 Context API의 개념에 대해 알아보도록 하자.


2. Context API

1️⃣ createContext()

createContext(initialValue);

- context 객체 생성

- createContext() 함수 호출 시 Provider와 Consumer Component 반환

- initialValue : Provider를 사용하지 않았을 때 적용될 초기값

 

 

2️⃣ <Context.Provider>

<Context.Provider>
...
</Context.Provider>

- 생성한 context를 하위 Component에 전달

 

 

3️⃣ useContext()

const ctx = useContext(context);

- context의 변화를 감시

- 설정한 상태를 불러올 때 사용


3. useContext()를 사용하지 않고 props로만 데이터 전달하기

우선 useContext()를 사용하지 않고 예제를 작성해보려고 한다.

App.js에 있는 데이터를 Meal Component로 props를 통해 데이터를 전달하려고 한다.

  📌 Component 경로 : App → Mart → Food → Meal

 

 

1️⃣ App.js

import { useState } from "react";
import Mart from "./components/Mart";

const DUMMY_MEALS = [
  {
    id: 'm1',
    name: 'Sushi',
    description: 'Finest fish and veggies',
    price: 22.99,
  },
  {
    id: 'm2',
    name: 'Schnitzel',
    description: 'A german specialty!',
    price: 16.5,
  },
  {
    id: 'm3',
    name: 'Barbecue Burger',
    description: 'American, raw, meaty',
    price: 12.99,
  }
];

const App = () => {
    const [items, setItems] = useState(DUMMY_MEALS);
    const addMeal = (newItem) => {
    	setItems([...items, newItem]);
        alert(`Add ${newItem}`);
    };
    
    return(
    	<>
            <Mart items={items} addMeal={addMeal} />
        </>
    );
};

export default App;

 

 

2️⃣ Mart.js

import Food from "./Food";

const Mart = props => {
	return(
    	<>
            <h1>Mart</h1>
            <Food items={props.items} addMeal={props.addMeal} />
        </>
    );
};

export default Mart;

 

 

3️⃣ Food.js

import Meal from "./Meal";

const Food = props => {
	return(
    	<>
            <h1>Food Corner</h1>
            <Meal items={props.items} addMeal={props.addMeal} />
        </>
    );
};

export default Food;

 

 

4️⃣ Meal.js

const Meal = props => {
    const newItem = {
        id: 'm4',
        name: 'Green Bowl',
        description: 'Healthy...and green...',
        price: 18.99,
    };
    
    return(
    	<>
            <h1>Meal Corner</h1>
            {props.items.map(item => {
                return (
                    <div key={item.id}>
                    	<h3>{item.name} : {item.price}</h3>
                        <p>{item.description}</p>
                    </div>
                );
            })}
            <button onClick={() => {props.addMeal(newItem)}}>+ Add</button>
        </>
    );
};

export default Meal;

 

위 예시는 Meal Component의 버튼을 클릭하면 새로운 아이템이 출력 하는 예시이다. Meal Component에서 데이터를 사용하기 위해 Mart, Food Component를 거쳐가고 있다. Mart, Food Component는 단지 데이터만 전달하고 있는 것이 매우 비효율적이다. 위 예시는 간단한 코드이지만 만약 하위 Component가 수십개라면 데이터 관리가 힘들고 유지보수 또한 어려울 것이다. 이제 전역적으로 데이터를 사용할 수 있는 useContext()를 통해 데이터를 접근해보자.


4. useContext() 사용하기

1️⃣ App.js

import { useState, createContext } from "react";
import Mart from "./components/Mart";

export const AppContext = createContext();

const DUMMY_MEALS = [
  {
    id: 'm1',
    name: 'Sushi',
    description: 'Finest fish and veggies',
    price: 22.99,
  },
  {
    id: 'm2',
    name: 'Schnitzel',
    description: 'A german specialty!',
    price: 16.5,
  },
  {
    id: 'm3',
    name: 'Barbecue Burger',
    description: 'American, raw, meaty',
    price: 12.99,
  }
];

const App = () => {
    const [items, setItems] = useState(DUMMY_MEALS);
    const addMeal = (newItem) => {
    	setItems([...items, newItem]);
        alert(`Add ${newItem}`);
    };
    
    return(
    	<AppContext.Provider value={{items, addMeal}}>
            <Mart />
        </AppContext.Provider>
    );
};

export default App;

 

 

2️⃣ Mart.js

import Food from "./Food";

const Mart = () => {
	return(
    	<>
            <h1>Mart</h1>
            <Food />
        </>
    );
};

export default Mart;

 

 

3️⃣ Food.js

import Meal from "./Meal";

const Food = () => {
	return(
    	<>
            <h1>Food Corner</h1>
            <Meal />
        </>
    );
};

export default Food;

 

 

4️⃣ Meal.js

import { useContext } from "react";
import { AppContext } from "../App";

const Meal = () => {
    const newItem = {
        id: 'm4',
        name: 'Green Bowl',
        description: 'Healthy...and green...',
        price: 18.99,
    };
    
    const { items, addMeal } = useContext(AppContext);
    
    return(
    	<>
            <h1>Meal Corner</h1>
            {items.map(item => {
                return (
                    <div key={item.id}>
                    	<h3>{item.name} : {item.price}</h3>
                        <p>{item.description}</p>
                    </div>
                );
            })}
            <button onClick={() => {addMeal(newItem)}}>+ Add</button>
        </>
    );
};

export default Meal;

 

Mart, Food Component를 거치지 않아도 useContext()를 사용하여 App Component에서 Meal Component로 데이터를 전달하였다.


💡 useContext() 사용할 때 주의할 점
Provider에서 제공한 value가 달라지면 useContext()를 사용하고 있는 모든 Component가 리렌더링된다.
그래서 useContext()를 사용할 때 value를 메모이제이션(Memoization) 하는 것에 신경 써야 한다.

 

 

📌 메모이제이션(Memoization)이 궁금하다면 아래 사이트를 참고하면 좋을 것 같다 !

 

[React] 리액트에서의 메모이제이션 (react memoization)

공부를 해도 자꾸 부족한 부분을 발견 기존 포스팅 수정하는 것보다 다시 정리하면서 작성하는 게 머리에 남을 것 같아서 재기록. 메모이제이션 연산의 결괏값을 메모리에 저장해 두고 이전 값

bbangaro.tistory.com

 

Comments