본문 바로가기
WEB/REACT

[React] ContextAPI를 사용해서 TodoList만들기(2) : contextAPI

by mingzoo 2021. 2. 9.

contextAPI에 대해서는 책 📖리액트를 다루는 기술📖에서도 다뤘지만 겉으로는 이해했지만, 몸과 마음 전체적으로 아직 이해를 못하고 깨닫지 못했다는 생각이 들어서 contextAPI를 사용한 예제인 투두리스트를 찾아서 다시해보게 되었다.

이번 포스트를 쓰면서 정한 내 목표는 이 포스트를 쓰면서 다 쓰고 나서는 내가 contextAPI가 뭔지 제대로 설명할 수 있고 알 수 있도록 만드는 것이다.

이전 포스트는 이 투두리스트를 만들면서 사용한 styled-components에 대한 요소들을 공부해본 포스트이다.

minjoo-space.tistory.com/56

 

[React] ContextAPI를 사용해서 TodoList만들기(1) : styled-components

리액트를 다루는 기술에서는 투두리스트를 만드는 예제를 만들어봤었는데, 그 때와 다르게 이번에는 contextAPI를 이용해서 투두리스트를 만들어보았다. 아직 contextAPI에 대해서 누구에게 설명할

minjoo-space.tistory.com

 

이 글은 공식문서 벨로퍼트님의 contextAPI를 사용한 투두리스트 만들기를 참고하여 작성한 글입니다.

 


Context

context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공해준다.

일반적으로는 부모가 자식에게 props를 전달하지만, 컴포넌트가 여러개가 되고 복잡해지면서 이 과정이 번거로워집니다. 이 과정에서 context를 사용하면 더 간단하게 구현해줄 수 있습니다.

 

이전 상태관리

처음에 만들었던 코드는 App 에서 todos 상태와, onToggle, onRemove, onCreate 함수를 지니고 있게 하고, 해당 값들을 props 를 사용해서 자식 컴포넌트들에게 전달해주는 방식으로 구현했다.

지금은 이렇게 구현해도 문제가 없지만, 프로젝트의 규모가 커지고 복잡해진다면 props를 전달해줘야하는 컴포넌트가 너어어어무 깊숙히 있게 될 수 있다.

그렇게 되면 너무 복잡해지기 때문에 context를 사용해서 아래 사진과 같이 구현할 수 있다.

context API를 사용한다면!

Context를 사용해서 전역 범위로 state를 관리할 수 있기 때문에 각 컴포넌트에서 Direct Access 가능


🤔여기서 잠깐! dispatch가 뭐지?

리덕스에 대한 포스트는 나중에 다시 다뤄볼 예정인데 dispatch가 뭔지 여기서 먼저 간단히 짚고 넘어가야겠다!

dispatch에 대해서는 useReducer에서도 언급이 됐었었다. 

const [state, dispatch] = useReducer(reducer, initialArg, init);

useReducer는 useState의 대체함수로, (state, action)=>newState의 형태로 reducer를 받고 dispatch 메서드와 짝 형태로 현재 state를 반환한다.

 

디스패치는 액션을 발생시키는 역할을 한다. 

액션을 발생시켜서 스토어에게 상태변화가 필요하다는 것을 알려준다!

그렇게 호출된 액션은 useReducer함수를 호출시키고 상태를 변화시키는 과정을 거치게 된다.

상태관리를 위한 것이라고 간단히 이해하자!

 

일단 간단한 이해는 여기까지하고 다시 코드로 돌아와보겠다.

자세한 내용은 리덕스를 정리한 글을 포스트하면서 정리하겠다!!

 


이 코드에서는 state  dispatch 를 Context 통하여 다른 컴포넌트에서 바로 사용 할 수 있게 해줄텐데, 우리는 하나의 Context 를 만들어서 state  dispatch 를 함께 넣어주는 대신에, 두개의 Context 를 만들어서 따로 따로 넣어준다. 이렇게 하면 dispatch 만 필요한 컴포넌트에서 불필요한 렌더링을 방지 할 수 있습니다. 추가적으로, 사용하게 되는 과정에서 더욱 편리하다고 한다.

 

 

TodoContext.js

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

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

function 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}`);
  }
}

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

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

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

export function useTodoState() {
  return useContext(TodoStateContext);
}

export function useTodoDispatch() {
  return useContext(TodoDispatchContext);
}

export function useTodoNextId() {
  return useContext(TodoNextIdContext);
}

context에서 사용할 값을 지정하기 위해서는 Provider컴포넌트를 렌더링하고 value를 정해주면된다.

<TodoDispatchContext.Provider value={dispatch}>
   {children}
</TodoDispatchContext.Provider>

 

이 코드처럼말이다.

그리고 props로 받아온 children값을 내부에 렌더링해주면 된다.

 

TodoContext 코드는 useConext를 3개로 분리했다.(State, Dispatch, nextId)

useContext로 해당 context를 넣어준다.

 

import { useTodoState } from '../TodoContext';
import { useTodoDispatch } from '../TodoContext';
...
 /* 커스텀 적용 : 커스텀 한 함수로 바로 사용 가능 */
 const todos = useTodoState();
 const dispatch = useTodoDispatch();

 /* 커스텀 미적용 : Context자체를 useContext의 인자로 넣음 */
 const todos = useContext(TodoStateContext);
 const dispatch = useContext(TodoDispatchContext);

useTodoState, useTodoDispatch등을 사용할 때,  useContext 를 사용하는 커스텀 Hook 을 만들어서 내보내주는 방법과 커스텀한 함수를 바로 사용하는 방법 2가지가 있는데 위의 코드와 같이 커스텀을 적용하고 안하고에 따라 달라진다.

 


완전히 다 이해해보려고했는데 아직 부족하게만 느껴진다. 빨리 리덕스 관련 포스트를 올려서 더 완벽히 이해할 수 있도록 해야겠다고 생각하게 됐다.😥

 

728x90

'WEB > REACT' 카테고리의 다른 글

[React] LifeCycle (라이프사이클, 생명주기)  (0) 2021.07.05
[React] Rerendering(리렌더링)  (0) 2021.06.24
[React] BrowserRouter VS HashRouter  (0) 2021.02.04
[React] styled-components  (4) 2021.02.03
[React] 영화 웹  (0) 2021.02.03