[React] Context API 구현하기

Nowod_K·2022년 6월 28일
0
post-custom-banner

출처 : 벨로퍼트와 함께하는 모던 리액트
https://react.vlpt.us/mashup-todolist/02-manage-state.html

벨로퍼트의 모던 리액트 강좌를 따라 열심히 투두리스트를 만드는 와중,
Context API 개념이 등장했다.

먼저 Context API를 도입하기전, 개인적으로 투두리스트의 기능을 나름대로 구현하였는데 이때 각 컴포넌트에서 발생하는 이벤트들을 최상단 컴포넌트로 가져오기 위해서 props를 중간중간 계속 전달해주었다.

그림으로 보면 이런 그림이 된다.

  • TodoItem에서 이벤트가 발생하게 되면 그 이벤트를 처리하기 위해 TodoList와 App에 모두 관련 함수를 전달해줘야 한다.

  • 3개의 컴포넌트에 함수를 연속적으로 넘겨주면서, 불필요한 동작들이 생긴다.

// APP 함수에서 checkDoneItem의 기능을 만들고 이를 TodoList로 전달한다.
const App = () => {
  const [todoItemArray, setTodoItemArray] = useState([]);

  const checkDoneItem = (id) => {

    const checkTodoItemArray = todoItemArray.map(
      item => {
        if (id === item.id) {
          item.done = !item.done;
        }
        return item;
      });
    setTodoItemArray(checkTodoItemArray);
  }

  return (
    <>
      <GlobalStyle />
        <TodoTemplate>
          <TodoList checkDoneItem={checkDoneItem} />
        </TodoTemplate>
    </>
  );
}

// TodoList에서 checkDoneItem 함수를 다시 TodoItem에 넘겨준다.
const TodoList = ({checkDoneItem}) => {
        return (
            <TodoItem
                checkDoneItem={checkDoneItem}
            >
            </TodoItem>
        )
    });
}

// TodoItem에서 checkDoneItem을 호출한다.
const TodoItem = ({checkDoneItem }) => {
    const handleCheckDone = () => {
        checkDoneItem();
    };

    return (
        <TodoItemBlock>
            <CheckCircle onClick={handleCheckDone}		   			 </CheckCircle>
        </TodoItemBlock>
    )

이러한 단점을 보완하기에 나온 것이 바로 Context API이다.

  • 하나의 API만 만들면 어느 컴포넌트에서든 한번에 접근할 수 있다.

useReducer와 useContext를 활용하면, provider로 선언된 내부에서는 해당 context를 접근할 수 있다.

import React, { createContext, useReducer, useRef } from "react";

const intitalTodos = [
    {
        id: 1,
        text: "프로젝트 생성하기",
        done: true,
    },
    {
        id: 2,
        text: "컴포넌트 스타일링하기",
        done: true,
    },
    {
        id: 3,
        text: "Context 만들기",
        done: false,
    }
];

const todoReducer = (state, action) => {
    switch (action.type) {
        case 'CREATE':
            return state.concat(action.todo);
        case 'TOGGLE':
            return state.map(todo =>
                todo.id === action.id ? { ...todo, done: !todo.done } : todo);
        case 'REMOVE':
            return state.filter(todo => todo.id !== action.id);
        default:
            throw new Error(`Unhandled action type: ${action.type}`);
    }
};

export const TodoStateContext = createContext();
export const TodoDispatchContext = createContext();
export const TodoNextIdContext = createContext();

export const TodoProvider = ({ children }) => {
    const [state, dispatch] = useReducer(todoReducer, intitalTodos);
    const nextId = useRef(5);

    return (
        <TodoStateContext.Provider value={state}>
            <TodoDispatchContext.Provider value={dispatch}>
                <TodoNextIdContext.Provider value={nextId}>
                    {children}
                </TodoNextIdContext.Provider>
            </TodoDispatchContext.Provider>
        </TodoStateContext.Provider>
    );
};


// 상단 app에서 context를 사용하면 하위 컴포넌트에서 다 활용 가능
const App = () => {
  const [todoItemArray, setTodoItemArray] = useState([]);

  const checkDoneItem = (id) => {

    const checkTodoItemArray = todoItemArray.map(
      item => {
        if (id === item.id) {
          item.done = !item.done;
        }
        return item;
      });
    setTodoItemArray(checkTodoItemArray);
  }

  // TodoProvider 선언
  return (
    <>
     <TodoProvider>
      <GlobalStyle />
        <TodoTemplate>
          <TodoList checkDoneItem={checkDoneItem} />
        </TodoTemplate>
     </TodoProvider>
    </>
  );
}

코드만 봤을 때는 복잡해 보일 수도 있지만, 프로젝트가 커지고 관리해야할 컴포넌트들이 늘어난다면, Context API는 굉장한 효용성을 제공한다.

profile
개발을 좋아하는 마음과 다양한 경험을 토대로 좋은 개발자가 되고자 노력합니다.
post-custom-banner

0개의 댓글