프롭 체인 없이, 리액트 내부적으로 state를 관리할 수 있게 해준다.
어떠한 컴포넌트든 state에 접근하여 저장된 내용을 바꾸고, 다른 컴포넌트들에도 수정사항을 전달할 수 있게 한다.
위 그림처럼 App.js 에서 header.js 를 import 하여 사용하고, header.js 에서 navigation.js 를 import 하여 사용한다고 가정하자.
지금까지는 프롭 체인을 통해 state를 부모 컴포넌트에서 자식 컴포넌트로 전달하여 사용할 수 있었다. 반대로 state 끌어올리기도 가능했다. 하지만 이 방법은 컴포넌트 간의 props 가 끊임없이 전달되지 않으면 사용할 수 없었다.
그래서 위 그림처럼 comtext 라는 개념으로 필요한 state 를 언제든지 어떤 컴포넌트에서도 호출하고 수정할 수 있게 만들었다고 생각한다.
만약 많은 정보들을 자식의 자식 컴포넌트에 props로 보내고 싶다면 자식 컴포넌트에서 또 그 자식 컴포넌트로 정보들을 일일이 props로 보내줘야 한다. 이럴때 context를 이용하면 코드가 간결해질 수 있다.
// auth-context.js
import React from 'react';
const AuthContext = React.createContext({
isLoggedIn: false,
onLogout: () => {},
});
export default AuthContext;
auth-context.js 라는 파일을 만들었다.
일반적인 컴포넌트와는 다르게 React.createContext로 만들어진다. 컴포넌트는 아니지만 컴포넌트의 역할을 한다.
이 AuthContext는 데이터를 담은 객체를 갖는데, 초기값으로 'isLoggedIn: false' 와 'onLogout: () => {}' 을 가진다. 이후 이 context를 공급하는 곳에서 초기값을 다시 변경해줄 수 있다.
onLogout의 값으로 더미 함수를 넣는 이유는 공급처에서 다시 데이터 객체 작성할 때, IDE 자동 완성을 편하게 하려고 작성했다.
// App.js
import React, { useState } from 'react';
import AuthContext from './auth-context';
import Header from './Header'
fucntion App () {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const logoutHandler = () => {
console.log('로그아웃 됐습니다.')
}
return (
<AuthContext.Provider value={{
isLoggedIn: isLoggedIn,
onLogout: logoutHanlder
}}>
<Header />
// Header.js 의 JSX 코드 안에 navigation 컴포넌트가 들어있다.
</ AuthContext.Provider>
}
App.js에서 AuthContext를 import 하고, Provider로 모든 요소들을 감싸줬다. 이때 value={} 으로 초기값을 다시 설정해주었다. 위해서 더미 함수를 넣어준 덕분에 IDE에서 자동완성이 가능했다.
이 Provider는 App.js의 모든 후손 컴포넌트들이 AuthContext의 state를 읽고, 쓰기를 가능하게 한다.
이제 이 공급받은 state를 후손의 후손 컴포넌트인 navigation.js에서 사용해보자.
// navigation.js
import React from 'react';
import AuthContext from './auth-context';
const navigation = () => {
return (
<AuthContext.Consumer>
{(ctx) => {
<div>
{ctx.isLoggedIn && (
<div>로그인 상태일 때 보이는 요소</div>
)}
</div>
}}
</AuthContext.Consumer>
)
}
공급(Provide)이 있었으니 소비(Comsume)할 차례이다.
모든 요소들을 AuthContext.Consumer로 감싸주었다. 그리고 JSX 코드 안에서 자바스크립트 구문을 사용하듯이 중괄호 안에 파라미터 값(ctx)를 갖는 익명 함수를 선언한다. 이제 AuthContext 에서 만들고 App.js 에서 수정한 state를 이 자손의 자손 컴포넌트에서 사용할 수 있다.
state 값을 가져오기 위해 ctx.isLoggedIn 처럼 파라미터로 받은 ctx를 통해 참조할 수 있다.
이제 이 방법보다 조금 더 간편한 방법을 사용해보자.
useContext는 React에서 지원하는 훅(Hook) 이다. 위에서 이용한 AuthContext를 사용하기 쉽게 해주는 녀석이다.
바로 써보자
// navigation.js
import React, { useContext } from 'react';
import AuthContext from './auth-context';
const navigation = () => {
const ctx = useContext(AuthContext); // useContext 사용
return (
<div>
{ctx.isLoggedIn && (
<div>로그인 상태일 때 보이는 요소</div>
)}
</div>
)
}
위에서 Consumer를 통해 받아온 것보다 훨씬 코드가 깔끔해졌다.
위에서 동작하는 것과 똑같이 아주 똑같이 동작한다.
useContext는 props를 대체할 수 있어 보인다. 아쉽지만 그렇지 않다.
짧은 시간동안 여러번 바뀌는 state 나 재사용해야 하는 컴포넌트에서는 useContext의 사용이 권장되지 않는다.