React.createContext()
이제 리액트에서 제공하는 Context API를 통해 Context를 어떻게 사용하는지에 대해서 알아보도록 하겠습니다. Context를 사용하기 위해서 가장 먼저 해야할 일은 Context를 생성하는 것입니다. Context를 생성하기 위해서 React.createContext()
함수를 사용합니다. 아래 예시 코드에 나온 것처럼 함수의 파라미터로 기본값을 넣어주면 됩니다. 그러면 Context객체가 만들어집니다.
const MyContext = React.createContext(기본값);
리액트에서 렌더링이 일어날 때, Context객체를 구독하는 하위 컴포넌트가 나오면 현재 Context의 값을 가장 가까운 상위 레벨에 있는 Provider
로부터 받아오게 됩니다. 그런데 만약 상위 레벨에 매칭되는 Provider
가 없다면, 그 경우에만 기본값이 사용됩니다. 때문에 기본값은 Provider
없이 컴포넌트를 테스트할 때 유용합니다. 참고로 기본값으로 undefined
를 넣으면 기본값이 사용되지 않습니다.
Context.Provider
React.createContext()
함수를 사용해서 Context를 만들었으면, 이제 하위 컴포넌트들이 해당 Context의 데이터를 받을 수 있도록 설정을 해줘야 합니다. 이를 위해서 사용하는 것이 바로 Provider
입니다. 영어 단어 Provider
는 제공자라는 뜻을 갖고 있습니다. 여기에서는 데이터를 제공해주는 컴포넌트라는 의미로 이해하면 됩니다. 모든 Context객체는 Provider
라는 리액트 컴포넌트를 갖고 있습니다. Context.Provider
컴포넌트로 하위 컴포넌트들을 감싸주면, 모든 하위 컴포넌트들이 해당 Context의 데이터에 접근할 수 있게 됩니다. Provider
는 아래 코드처럼 사용하면 됩니다.
<MyContext.Provider value={/* some value */}>
Provider
컴포넌트에는 value
라는 prop
이 있으며, 이것은 Provider
컴포넌트 하위에 있는 컴포넌트들에게 전달됩니다. 그리고 하위 컴포넌트들이 이 값을 사용하게 되는데, 이 하위 컴포넌트들을 데이터를 소비한다는 뜻에서 consuming 컴포넌트라고 부릅니다. 그리고 이 consuming 컴포넌트들은 Context의 값의 변화를 지켜보다가, 만약 값이 변경되면 재렌더링됩니다. 참고로 하나의 Provider
컴포넌트는 여러 개의 consuming 컴포넌트와 연결될 수 있으며, 여러 개의 Provider
컴포넌트는 중첩되어 사용될 수 있습니다.
Provider
컴포넌트로 감싸진 모든 consuming 컴포넌트들은 Provider
의 value prop
이 바뀔 때마다 재렌더링됩니다. 값이 변경되었을 때 만약 상위 컴포넌트가 업데이트 대상이 아니더라도, 하위에 있는 컴포넌트가 Context를 사용한다면 하위 컴포넌트에서는 업데이트가 일어납니다. 이 때 값의 변화를 판단하는 기준은 자바스크립트 객체의 Object.is
라는 함수와 같은 방식으로 판단합니다. Object.is
에 대한 자세한 내용이 궁금하면 아래 링크를 참고하기 바랍니다.
NOTE. Provider value에서 주의해야 할 사항
Context는 재렌더링 여부를 결정할 때 레퍼런스 정보를 사용하기 때문에,Provider
의 부모 컴포넌트가 재렌더링 되었을 경우 의도치 않게 consumer 컴포넌트가 재렌더링이 일어날 수 있는 문제가 있습니다. 예를 들어, 아래 코드는Provider
컴포넌트가 재렌더링 될 때마다 모든 하위 consumer 컴포넌트들을 재렌더링 하게 됩니다. 왜냐하면value prop
을 위한 새로운 객체가 매번 새롭게 생성되기 때문입니다.
function App(props) { return ( <MyContext.Provider value={{ something: 'something' }}> <Toolbar /> </MyContext.Provider> ); }
이를 방지하기 위해서는
value
를 직접 넣는 것이 아닌, 컴포넌트의state
로 옮기고 해당state
의 값을 넣어주어야 합니다. 아래 코드는 수정한 이후의 모습입니다.
function App(props) { const [value, setValue] = useState({ something: 'something' }); return ( <MyContext.Provider value={value}> <Toolbar /> </MyContext.Provider> ); }
Class.contextType
Class.contextType
은 Provider
의 하위에 있는 클래스 컴포넌트에서 Context의 데이터에 접근하기 위해서 사용하는 것입니다. 클래스 컴포넌트는 현재 거의 사용하지 않기 때문에 이런 방법이 있다는 것만 참고하기 바랍니다.
아래 코드에 나와 있는 것처럼 MyClass.contextType = MyContext;
라고 해주면, MyClass
라는 클래스 컴포넌트는 MyContext
의 데이터에 접근할 수 있게 됩니다.
class MyClass extends React.Component {
componentDidMount() {
let value = this.context;
/* MyContext의 값을 이용하여 원하는 작업을 수행 가능 */
}
componentDidUpdate() {
let value = this.context;
/* ... */
}
componentWillUnmount() {
let value = this.context;
/* ... */
}
render() {
let value = this.context;
/* MyContext의 값에 따라서 컴포넌트들을 렌더링 */
}
}
MyClass.contextType = MyContext;
클래스 컴포넌트에 있는 contextType
속성에는 React.createContext()
함수를 통해 생성된 context객체가 대입될 수 있습니다. 이 속성을 사용하게 되면 this.context
를 통해 상위에 있는 Provider
중에서 가장 가까운 것의 값을 가져올 수 있습니다. 또한 위 예시 코드에 나와 있는 것처럼 render()
함수를 포함한 모든 라이프사이클 함수 어디에서든지 this.context
를 사용할 수 있습니다.
그리고 참고로 이 API를 사용하면 단 하나의 Context만을 구독할 수 있습니다. 여러 개의 Context를 동시에 사용하는 방법에 대해서는 뒤에서 다루도록 하겠습니다.
Context.Consumer
consumer 컴포넌트는 앞에서 설명한 것처럼 Context의 데이터를 구독하는 컴포넌트입니다. 클래스 컴포넌트에서는 위에 나온 Class.contextType
을 사용하면 되고, 함수 컴포넌트에서는 Context.Consumer
를 사용하여 Context를 구독할 수 있습니다. 아래는 예시 코드입니다.
<MyContext.Consumer>
{value => /* context의 값에 따라서 컴포넌트들을 렌더링 */}
</MyContext.Consumer>
컴포넌트의 자식으로 함수가 올 수 있는데, 이것을 function as a child라고 부릅니다. Context.Consumer
로 감싸주면 자식으로 들어간 함수가 현재 Context의 value
를 받아서 리액트 노드로 리턴을 하게 됩니다. 이때 함수로 전달되는 value
는 Provider
의 value prop
과 동일합니다. 만약 상위 컴포넌트에 Provider
가 없다면, 이 value
파라미터는 createContext()
를 호출할 때 넣는 기본값과 동일한 역할을 합니다.
NOTE. function as a child
function as a child는 컴포넌트의 자식(child)으로 함수를 사용하는 방법입니다. 리액트에서는 기본적으로 하위 컴포넌트들을children
이라는prop
으로 전달해주는데,children
으로 컴포넌트대신 함수를 사용하여 아래와 같이 사용할 수 있습니다.
// children이라는 prop을 직접 선언하는 방식 <Profile children={name => <p>이름: {name}</p>} /> // Profile컴포넌트로 감싸서 children으로 만드는 방식 <Profile>{name => <p>이름: {name}</p>}</Profile>
Context.displayName
Context객체는 displayName
이라는 문자열 속성을 가집니다. 그리고 크롬의 리액트 개발자 도구에서는 Context의 Provider
나 Consumer
를 표시할 때 이 displayName
을 함께 표시해줍니다. 예를 들어, 아래와 같이 코드를 작성하면 MyDisplayName
이 리액트 개발자 도구에 표시됩니다.
const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';
// 개발자 도구에 "MyDisplayName.Provider"로 표시됨
<MyContext.Provider>
// 개발자 도구에 "MyDisplayName.Consumer"로 표시됨
<MyContext.Consumer>
마지막 업데이트: 2025년 08월 28일 09시 18분
이 문서의 저작권은 이인제(소플)에 있습니다. 무단 전재와 무단 복제를 금합니다.