[React] Hooks(3) useContext, useReducer

JiHyun·2023년 3월 3일
0
post-thumbnail

별코딩 React Hook강의
본 내용은 별코딩님의 React Hook강의를 참고하여 작성하였습니다.


useContext

리액트에서의 데이터 흐름은 부모 컴포넌트에서 자식 컴포넌트로 props를 통해 이동한다.
props로 전달할땐 일일이 태그를 붙여주고 '단계별로' 전달해야하고(이러한 과정을 Prop Driling이라고 한다.) 데이터가 필요하지 않은 중간 컴포넌트들도 하위 컴포넌트에게 데이터를 전달하기위해 데이터를 넘겨받아야 한다.
전달하는 과정에서 데이터를 잘못 전달하거나 데이터를 수정해버리면 컴포넌트별로 다 찾아서 오류를 해결해줘야하는 번거로움이 생길뿐더러 만일 앱이 더 커지고 component들이 엄청나게 많아지면 문제가 매우 심각해질 수 있다.
이러한 문제점을 간편하게 해결해주는 것이 바로 ’context API‘ 이다.
context는 앱 안에서 전역으로 사용되는 데이터를 여러 컴포넌트들끼리 쉽게 접근할 수 있게 해준다. context를 사용하면 일일이 props로 데이터를 넘겨주지 않아도 되고 데이터가 필요하지 않은 중간 컴포넌트한테까지 굳이 전달하지 않아도 된다.

하지만 context는 꼭 필요할때만 사용해야한다. context를 사용하면 컴포넌트를 재사용하기 어려워질 수 있기 때문이다. 리액트에서는 만일 context의 사용 목적이 Prop Driling을 피하기 위한 목적이라면 Component Composition을 먼저 고려해보라고 제안한다.

context API의 필수개념

  • createContext : context 생성
  • consumer : context 변화 감지
  • provider : 하위 컴포넌트에게 context 전달

context를 사용하려면 데이터를 관리하기 위한 context파일을 하나 만들고 createContext를 import 해야 한다.

import { createContext } from "react";

export const context변수명 = createContext(초기값);

Context의 Provider는 value라는 prop을 하나 받고 value안에는 전달하고자하는 데이터를 넣어주면 된다. 그리고 컴포넌트에서 해당 데이터를 사용할땐 context의 변수 이름을 import 하고 useContext로 변수를 지정해주면 된다

import { context변수명 } from "경로";

// ... 함수내부
  const { isDark, setIsDark } = useContext(context변수명);

useReducer

component에 state를 생성하고 관리하는데 useState말고도 useReducer를 사용할 수도 있다.
그럼 어떨때 useReducer를 사용할까?
여러개의 하위 값을 포함하는 복잡한 state를 다뤄야할때 useState 대신 useReducer를 사용하면 코드가 훨씬 더 깔끔하고 유지보수에 효율적이다.

useReducer 는 Reducer, Dispatch, Action 으로 이루어져 있다.
강의에서는 이 세가지를 은행 업무로 비유했다. state는 계좌, Reducer는 은행, Dispatch 는 계좌의 소유주의 요구사항, Action 은 요구사항에 대한 내용이다.
계좌의 소유주가 은행에 10000원을 출금해달라(Action)는 요구(Dispatch)를 하면 은행(Reducer)은 요구에따라 계좌내역(state)을 변경한다.

  • Reducer : state를 변경
  • Dispatch : 함수
  • Action : Dispatch의 인자

useReducer는 useState와 비슷하게 배열을 반환해준다. 그리고 useReducer는 인자를 2개로 받는데 첫번째 인자로는 함수를, 두번째 인자로는 초기값을 받는다. 여기서 useReducer가 반환해준 배열의 두번째 요소를 함수로 호출하면 useReducer의 첫번째 인자의 함수 reducer가 실행되고 그 함수의 두번째 인자인 action이 dispatch의 인자로 들어가게된다.

const reducer = (state, action) => {
	console.log(state) // 0
  	console.log(action) // { type: ... , payload: ... }
  
  return state + action.payload;
};

// ... 함수내부
      // state, 함수                // 함수, 초기값
const [money, dispatch] = useReducer(reducer, 0);

여기서 reducer의 인자인 state는 money의 초기값인 0을 받고 action은 dispatch함수를 호출할때 인자로 넣은 값(대개는 객체형태임)이 들어간다. 그리고 리턴값이 바로 money의 값이 되는 것이다.

리턴값에 if/else문 혹은 switch문을 사용해서 리턴한다. 그리고 그 값 어디든 해당되지 않을 경우를 대비해서 state를 리턴한다는 코드가 꼭 있어야 에러를 방지할 수 있다.

const reducer = (state, action) => {
  switch (action.type) {
    case "deposit":  // action의 객체에서 type이 'deposit'일때
      return state + action.payload;
    case "withdraw":   // action의 객체에서 type이 'withdraw'일때
      return state - action.payload;
    default: 
      return state;    // 에러방지용
  }
};

처음 강의만 들을때는 이해가 전혀 안되고 어려웠는데 useReducer의 과정(?)을 하나하나 따라가면서 이게 어떤건지 인지하려고 하니 이해가 조금씩 되었다. 사실 이정도 코드에선 useState를 사용하는 것이 더 나을 수 있지만 이보다 코드가 더 복잡해진다면 useReducer를 사용하는게 더 효율적인 코드가 될것이다.

profile
비전공자의 개발일기📝

0개의 댓글