React

 

 

리액트에서 컨텍스트(Context)와 리듀서(Reducer)를 사용하는 방법

 

import React, { createContext, useReducer } from 'react';

// 카운터 컨텍스트를 생성합니다. 초기 상태는 0입니다.
const CounterContext = createContext({
  count: 0,
  increment: () => {},
  decrement: () => {},
});

// 카운터 상태를 관리하는 리듀서 함수입니다.
function counterReducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { count: state.count + 1 };
    case 'DECREMENT':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

// CounterContextProvider 컴포넌트는 카운터 상태를 관리하고, 자식 컴포넌트에게 카운터 컨텍스트를 제공합니다.
export function CounterContextProvider({ children }) {
  const [state, dispatch] = useReducer(counterReducer, { count: 0 });

  // 카운터를 증가시키는 함수입니다.
  function increment() {
    dispatch({ type: 'INCREMENT' });
  }

  // 카운터를 감소시키는 함수입니다.
  function decrement() {
    dispatch({ type: 'DECREMENT' });
  }

  const counterContext = {
    count: state.count,
    increment,
    decrement,
  };

  // CounterContext.Provider 컴포넌트를 반환합니다. value prop에는 카운터 컨텍스트를 전달합니다.
  return (
    <CounterContext.Provider value={counterContext}>
      {children}
    </CounterContext.Provider>
  );
}

export default CounterContext;

 

위 코드는 카운터 애플리케이션을 구현한 것입니다. 카운터의 상태를 증가시키거나 감소시키는 기능을 제공합니다. 이 컨텍스트는 카운터 상태를 관리하고, 해당 상태를 필요로 하는 컴포넌트에게 제공합니다. 이를 통해 상태 관리를 중앙에서 할 수 있으며, 상태를 필요로 하는 여러 컴포넌트가 재렌더링 없이 상태를 공유할 수 있습니다. 이 방식은 상태 관리 라이브러리인 Redux의 기본 개념과 유사합니다.

컨텍스트(Context)는 React 컴포넌트 트리 안에서 전역적(global)이라고 볼 수 있는 데이터를 공유할 수 있도록 고안된 방법입니다. 컨텍스트를 사용하면 중간에 있는 엘리먼트들을 통해 props를 넘겨주지 않고도 컴포넌트 트리 내에서 하위 컴포넌트들이 값을 사용할 수 있습니다.

리듀서(Reducer)는 현재의 상태와 액션 객체를 파라미터로 받아와서 새로운 상태를 반환해주는 함수입니다. 리듀서 함수에서 새로운 상태를 만들 때는 반드시 불변성을 지켜주어야 합니다. 이전 상태는 절대로 건드리지 않고, 변화를 줄 상태의 사본을 만들어서 그 사본에 변화를 주고, 그 사본의 상태를 새로운 상태로 설정해주어야 합니다. 이를 통해 상태가 언제, 왜, 어떻게 업데이트되었는지 쉽게 추적하고 이해할 수 있습니다. 또한, 성능 최적화에도 큰 도움이 됩니다.

이런 방식으로 컨텍스트와 리듀서를 사용하면 상태 관리 로직을 컴포넌트 외부에 작성할 수 있고, 여러 컴포넌트에서 상태를 공유하거나 상태를 조작하는 함수를 재사용할 수 있습니다. 이는 코드의 재사용성을 높이고, 유지 보수를 용이하게 합니다. 또한, 상태 관리 로직을 분리함으로써 컴포넌트의 역할을 명확하게 하고, 가독성을 높일 수 있습니다.

이렇게 컨텍스트와 리듀서를 사용하면 상태 관리를 효과적으로 할 수 있습니다. 이는 큰 규모의 프로젝트에서 특히 유용합니다. 하지만 작은 규모의 프로젝트에서는 굳이 이런 방식을 사용하지 않아도 됩니다. 상태를 props로 넘겨주는 것만으로도 충분히 상태 관리를 할 수 있습니다. 하지만 프로젝트의 규모가 커지면 커질수록 상태 관리가 복잡해지므로, 이런 방식을 사용하는 것이 좋습니다.

 

 

 

 

 

장바구니 예

1. CartContext.jsx

import { createContext, useReducer } from "react"

// 장바구니 컨텍스트를 생성합니다. 초기 상태는 빈 아이템 배열입니다.
const CartContext=createContext({
    items:[],
    addItem:(item)=>{},
    removeItem:(id)=>{},
});

// 장바구니 상태를 관리하는 리듀서 함수입니다.
function cartReducer(state, action){
    if(action.type==='ADD_ITEM'){
        // 기존 장바구니에 동일한 상품이 있는지 확인합니다.
        const existingCartItemIndex=state.items.findIndex((item)=>item.id===action.item.id);

        // 장바구니 아이템 배열을 복사합니다.
        const updatedItems=[...state.items];
        
        if(existingCartItemIndex >-1){
            // 장바구니에 동일한 상품이 있으면, 해당 상품의 수량을 1 증가시킵니다.
            const existingItem=state.items[existingCartItemIndex];
            const updateItem={
                ...existingItem,
                quantity:existingItem.quantity+1,
            };

            updatedItems[existingCartItemIndex]=updateItem;
        }else{
            // 장바구니에 동일한 상품이 없으면, 새로운 상품을 추가합니다.
            updatedItems.push({...action.item,quantity:1});
        }

        return { ...state,items:updatedItems}        
    }

    if(action.type==='REMOVE_ITEM'){
        // 삭제할 상품의 인덱스 번호를 가져옵니다.
        const existingCartItemIndex=state.items.findIndex((item)=>item.id===action.id);      
        const existingCartItem=state.items[existingCartItemIndex];

        const updatedItems=[...state.items];
        if(existingCartItem.quantity===1){            
            // 상품의 수량이 1개면, 해당 상품을 삭제합니다.
            updatedItems.splice(existingCartItemIndex, 1);
        }else{
            // 상품의 수량이 1개 이상이면, 상품의 수량을 1개 줄입니다.
            const updatedItem={
                ...existingCartItem,
                 quantity:existingCartItem.quantity-1,
            }
            updatedItems[existingCartItemIndex]=updatedItem;
        }

        return { ...state,items:updatedItems}        
    }
    return state;
}

// CartContextProvider 컴포넌트는 장바구니 상태를 관리하고, 자식 컴포넌트에게 장바구니 컨텍스트를 제공합니다.
export  function CartContextProvider({children}){          
    const [cart, dispatchCartAction] =useReducer(cartReducer,{items:[]});

    // 상품을 장바구니에 추가하는 함수입니다.
    function addItem(item){
        dispatchCartAction({type:'ADD_ITEM',item});
    }

    // 장바구니에서 상품을 제거하는 함수입니다.
    function removeItem(id){
        dispatchCartAction({type:'REMOVE_ITEM',id});
    }

    const cartContext={
        items:cart.items,
        addItem,
        removeItem
    }

    console.log(cartContext);

    // CartContext.Provider 컴포넌트를 반환합니다. value prop에는 장바구니 컨텍스트를 전달합니다.
    return(
        <CartContext.Provider value={cartContext}>
            {children}
        </CartContext.Provider>
    )
}

// CartContext를 export 합니다. 다른 컴포넌트에서 이 컨텍스트를 사용할 수 있습니다.
export default  CartContext;

 

 

 

위 코드는 React와 useReducer 훅을 사용하여 장바구니 기능을 구현한 것입니다. 장바구니에 상품을 추가하거나 제거하는 기능을 제공합니다.

이 컨텍스트는 장바구니 상태를 관리하고, 해당 상태를 필요로 하는 컴포넌트에게 제공합니다. 이를 통해 상태 관리를 중앙에서 할 수 있으며,

상태를 필요로 하는 여러 컴포넌트가 재렌더링 없이 상태를 공유할 수 있습니다. 이 방식은 상태 관리 라이브러리인 Redux의 기본 개념과 유사합니다.

 

 

 

 

2. 사용방법

1)설정 <CartContextProvider>  로 감싸준다.

import Header from "./components/Header";
import Meals from "./components/Meals";
import { CartContextProvider } from "./store/CartContext";

function App() {
  return (
    <CartContextProvider>
      <Header />
      <Meals />
   </CartContextProvider>
  );
}

export default App;

 

 

2). useContext 

const cartCtx=useContext(CartContext);

 

import { useContext } from "react";
import { currencyFormatterKR } from "../utils/formatting";
import Button from "./Button";
import CartContext from "../store/CartContext";

export default function MealItem({meal}) {
 
  const cartCtx=useContext(CartContext);


  function handleAddMealToCart(){
    cartCtx.addItem(meal);
  }

  return (
    <li>      
            <p>
              <Button  onClick={handleAddMealToCart}  >장바구니 추가</Button>
            </p>
       
    </li>
  )

}

 

 

 

3) 장바구니 전체 수량

import { useContext } from 'react';
import logImg from  '../assets/logo.jpg';
import Button from './Button';
import CartContext from '../store/CartContext';


export default function Header() {

  const cartCtx=useContext(CartContext);

  //reduce 의 첫번째 인자값은 총항목을 표시
  const totalCartItems =cartCtx.items.reduce((totalNumberOfItems, item)=>{
      return totalNumberOfItems+item.quantity;
  }, 0);




  return (
    <header id="main-header">
        <div id="title">
            <img src={logImg}   />
             <h1>리액트 푸드</h1>
        </div>
        <nav>
            <Button textOnly    >장바구니(0)</Button>
        </nav>
    </header>
  )
}

 

 

 

 

 

 

소스 :

https://github.com/braverokmc79/macaronics-react-food

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

He who hesitate is lost. (망설이는 자는 모든 것을 잃는다.)(쇠뿔도 단 김에 빼라.)

댓글 ( 0)

댓글 남기기

작성