React

[React] useContext Hook의 사용법 및 상태 관리 라이브러리와의 차이

킹우현 2023. 4. 12. 15:33

context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있습니다.
context는 React 컴포넌트 트리 안에서 전역적(global)이라고 볼 수 있는 데이터를 공유할 수 있도록 고안된 방법입니다.
context의 주된 용도는 다양한 레벨에 네스팅된 많은 컴포넌트에게 데이터를 전달하는 것입니다. (출처: React 공식 문서)

1) useContext Hook 이란?

부모 컴포넌트에서 자식 컴포넌트에게 데이터를 전달할 때, 보통 props를 통해 데이터를 전달하는데, 그 깊이가 깊어진다면 거쳐가야 하는 컴포넌트도 많아진다. 또한 해당 props를 사용하지 않는 컴포넌트까지 중간에 거쳐가게 되는 상황이 올 수 있다.

 

그렇게 된다면 코드를 반복적으로 작성해야 하는 일도 많아지고, 변수명이 바뀌면 거쳐가는 모든 컴포넌트에서 변수명을 수정해야 하거나 에러를 추적하기 어려운 등 유지보수성이 낮아진다는 문제가 발생한다.

👉🏻 Prop Drilling : props를 오로지 하위 컴포넌트로 전달하는 용도로만 쓰이는 컴포넌트들을 거치면서 React Component 트리의 한 부분에서 다른 부분으로 데이터를 전달하는 과정

 

이런 비효율적인 문제를 해결하는데에 Context가 꽤나 유용하다. Context를 사용하면 전역적으로 데이터를 공유하기 때문에 데이터가 필요한 컴포넌트에서 바로 사용이 가능하게 된다.

 

React Hook인 useContext()는 Context를 좀 더 편하게 사용할 수 있게하는데, 우선 Context API의 개념에 대해 알아보도록 하자.

 

2) Context API

import { createContext } from "react";

export const AppContext = createContext();

1. createContext() : context 객체 생성, createContext() 함수 호출 시 Provider와 Consumer Component 반환

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

 

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

2. <Context.Provider> : 생성한 context를 하위 Component에 전달

 

const { items, addMeal } = useContext(AppContext);

3. useContext() : context의 변화를 감시(구독), 설정한 전역 상태를 불러올 때 사용

 

useContext() 사용할 때 주의할 점 🚨
Provider에서 제공한 value가 달라지면 useContext()를 사용하고 있는 모든 Component가 리렌더링된다.

 

3) Context의 단점과 상태 관리 라이브러리(Recoil)를 사용하는 이유

context는 상태값이 달라지면 구독하고 있는 하위 컴포넌트가 모두 다시 랜더링이 된다. 이를 해결하기 위해서는 Context를 분리해서 사용하는 것인데 이렇게 되면 코드를 작성하기 불편하고, Provider Hell에 빠져 코드가 상당히 복잡해진다. ⭐️⭐️⭐️

 

만약에 단순히 props drilling 해결이 목적이고, 렌더링 최적화가 필요없는 상태(테마, 나이트모드)라면 Context로 충분히 해결할 수 있다.

Recoil 이란 ?

Recoil은 페이스북에서 만든 상태관리 라이브러리로, 아직 안정화 단계에 있는 단계는 아니지만 사용을 고려할만한 다음과 같은 장점을 가지고 있다.

 

1. 적은 코드량, 쉬운 러닝 커브 : Recoil은 상태 하나를 atom으로 정의하고, 그것을 구독하는 패턴이다. 또한 이런 atom들로부터 파생된 상태를 selector로 선언한다. 이 selector들은 구독한 atom이 변화하면 자동적으로 변화한다.

따라서 데이터 흐름이 직관적이다. 페이스북에서 만든 만큼 리액트와도 매우 잘어울려서 훅을 사용해보았다면 쉽게 배울 수 있다.

 

2. 렌더링 최적화 : Context에서는 상태변경시 구독한 하위 컴포넌트들이 모두 재렌더링 되는 문제가 있었는데, Recoil은 atom, selector를 구독하면 구독한 컴포넌트만 재렌더링이 일어난다.

 

3. 간편한 비동기 처리 : 무엇보다, Redux에서는 추가적인 api로 구현되었던 비동기 처리나 캐싱을 Recoil에서는 자체적으로 selector에서 지원을 한다. 에러처리도 리액트의 suspense를 사용할 수 있고, useRecoilValueLoadable을 사용한다면, hasValue, loading, hasError 상태를 제공해서, 로딩완료시, 로딩시, 에러시 분기처리가 손쉽게 가능하다.