React 에서 컴포넌트가 데이터를 다루는 방법으로는 props, state 그리고 context 가 있습니다.
이번에는 흔히 다루는 props, state 말고, context의 개념과 사용 방법 그리고 연관되어 있는 hook 인 useContext 에 대하여 공부해보도록 하겠습니다.
우선 리액트 공식 문서에 있는 useContext 의 설명을 보면
context 객체(React.createContext에서 반환된 값) 을 받아 해당 context 의 현재 값을 반환 합니다.
위 개념을 이해하기 쉽게 props 와 state , react 에서 데이터가 어떠한 방식으로 전달되는지 알아 봅시다.
먼저 props 와 state 의 용도 ❓
부모 컴포넌트와 자식 컴포넌트 또는 하나의 컴포넌트 안에서 데이터를 다루기 위해 사용 합니다.
이 때, props 와 state 를 사용하게 되면 부모 컴포넌트 ➡️ 자식 컴포넌트 즉, 위에서 아래로 흐르듯이 한쪽으로 데이터가 흐르게 됩니다.
만약 컴포넌트 한쪽에서 흐르고 있는 데이터를 필요로 인해 반대편으로 흐름에서도 사용하고 싶다면 ❓
React 에서 데이터는 단방향으로 위에서 아래로, 즉 부모 컴포넌트에서 자식 컴포넌트로 전달 하기 때문에 데이터를 사용하고 싶은 컴포넌트들의 공통 부모 컴포넌트에 state 를 만들고 props 로 전달 할 수 있습니다.
하지만, 컴포넌트 트리의 구조가 더 복잡하고 깊어지면❓ props drilling 문제가 발생 할 수 있습니다.
props drilling 은 ❓
props를 오로지 하위 컴포넌트로 전달하는 용도로만 쓰이는 컴포넌트들을 거치면서 React Component 트리의 한 부분에서 다른 부분으로 데이터를 전달하는 과정입니다.
props 로 컴포넌트 간 데이터를 전달하는건 지극히 정상적인 방법 입니다. 하지만 문제는
해당 props 를 사용하지 않는 컴포넌트에도 전달을 해야 합니다.
깊어 질 수록 코드를 읽을 때 해당 props 을 추적하기 힘들어집니다.
의도치 않은 리렌더링
1번의 경우 예를 들어 해당 data 는 F 에서 사용하기 위해서 A 와 D 를 거쳐 가야 합니다.
2번의 경우 위와 같은 상황이면 충분히 넘길 수 있다고 생각 하지만, 만약 5개 컴포넌트만 넘어가도 복잡해지고 추적하기 힘들 것 입니다.
그리고 3번의 경우 react 에서 리렌더링이 일어나는 경우를 기억하시나요❓ 새로운 props 값이 전달되거나, 부모 컴포넌트가 렌더링 될 때 자식 컴포넌트 또한 리렌더링 됩니다!
이처럼 하나의 컴포넌트에 데이터를 전달하기 위해 props 를 전달하는 깊이가 깊어지면
의도치 않은 렌더링으로 인한 side effect 가 발생 할 수 있습니다!
이러한 문제를 해결하기 위해 context API 가 하나의 대안이 될 수 있습니다.
context 는 ❓
부모 컴포넌트에서 자식 컴포넌트로 전달되는 데이터의 흐름과는 상관없이
전역적인 데이터를 다룰 때 사용 합니다.
위와 같이 context 라는 별도의 공간에 전역적으로 사용하기 위한 데이터를 저장하고,
props 로 부모 컴포넌트에서 하위 컴포넌트까지 데이터를 전달 하지 않고
필요로 하는 컴포넌트에서 직접 전달하여 사용 할 수 있습니다.
context 를 사용하기 위해서는 context API 를 사용해야 하며 createContext, Provider, Consumer 등에 대해 알아야 합니다.
createContext : Context 객체를 만든다.
Provider : 생성된 Context 의 data 를 하위 컴포넌트에 전달하는 역할
Consumer : Context 의 변화를 감시하는 역할
✅ 리액트에서 context 를 사용하기 위한 단계
createContext 를 사용해 Context 를 생성 합니다
생성된 Context 를 가지고 Context Provider 로 컴포넌트 트리를 감싸 줍니다.
value props 를 사용해 Context Provider 에 원하는 값을 입력 합니다.
Context Consumer 를 통해 필요한 컴포넌트에 그 값을 불러 옵니다.
Context 예시
앞서 4 번의 단계를 생각하면서 코드를 보면 금방 이해 할 수 있습니다!
먼저 App.js 컴포넌트에서
createConetxt() 를 사용해 context 를 만들고
AppContext
변수에 결과를 담아두었습니다.이렇게 만들어진 context 객체는 Provider 와 Consumer 라는 속성을 갖는 객체 입니다.
다른 컴포넌트에게 값을 내려주기 위해서 Provider 를 사용해 감싸줍니다.
AppContext.Provider
에는 컴포넌트 트리에 전달해주고 싶은 값을 넣습니다.(value={user}
)
그 다음 데이터를 받는 Childern.js 컴포넌트 에서는
1. context 에서 제공하는 값을 사용하고 싶은 컴포넌트에서는 Consumer 를 사용해 감싸 줍니다.
2. 그 후, 함수의 결과 값으로 value 를 반환해 사용할 수 있습니다.
결과
context API 로 할 수 있는데 useContext 를 사용하는 이유는 ❓
useContext 를 사용하면 return 구문에서 같은 태그를 사용하는 불편함이 줄어들고 context 를 통해 값을 변수에 저장해서 바로 사용 할 수 있습니다.
코드가 보기 좋고 간결하게 작성 할 수 있습니다.
참고
https://velog.io/@ricky0813/React-Prop-Drilling
https://velog.io/@jminkyoung/TIL-6-React-Hooks-useContext-%EB%9E%80
한입 크기로 잘라 먹는 리액트(React.js) : 기초부터 실전까지