이번 편에서는 React Context를 조금 더 편하게 사용할 수 있는 useContext를 알아보겠습니다.
import React, { createContext } from 'react'
// 0. AppContext 생성
const AppContext = createContext()
const App = () => {
const user = {
nickname: 'danuel',
isAdmin: true
}
return (
<AppContext.Provider value={user}>
<div>
<Posts />
</div>
</AppContext.Provider>
)
}
// 1. PostsContext 생성
const PostsContext = createContext()
const Posts = () => {
const posts = [
{
title: 'useContext 알아보기',
content: '이번 편에서는 React Context를 ...'
}
]
return (
<PostsContext.Provider value={posts}>
<Children />
</PostsContext.Provider>
)
}
// 2. user와 posts를 가져와 화면에 보여주기
const Children = () => (
<AppContext.Consumer>
{user => (
<PostsContext.Consumer>
{posts => {
let label = 'user'
if (user.isAdmin) {
label = 'admin'
}
return (
<div>
<div>{label}</div>
<div>{user.nickname}</div>
<div>{posts.map((post, index) => (
<div key={index}>
<div>{post.title}</div>
<div>{post.content}</div>
</div>
))}</div>
</div>
)
}}
</PostsContext.Consumer>
)}
</AppContext.Consumer>
)
Children 컴포넌트가 user와 post를 모두 보여주는 억지스러운 예시이지만, 쉽게 이해할 수 있도록 간단하게 표현했습니다.
React Context는 Props Drilling 패턴을 떨칠 수 있고 유연한 컴포넌트를 작성할 수 있다는 부분에서 좋은 기능인 것은 분명하지만, 여러 Context를 중첩해서 사용하다 보면 간단 기능을 담당하는 컴포넌트가 점점 이상하게 변하기 시작합니다. 더 나은 방법이 필요하다는 생각이 드는 순간입니다.
import React, { createContext, useContext } from 'react'
// 0. AppContext 생성
const AppContext = createContext()
const App = () => {
const user = {
nickname: 'danuel',
isAdmin: true
}
return (
<AppContext.Provider value={user}>
<div>
<Posts />
</div>
</AppContext.Provider>
)
}
// 1. PostsContext 생성
const PostsContext = createContext()
const Posts = () => {
const posts = [
{
title: 'useContext 알아보기',
content: '이번 편에서는 React Context를 ...'
}
]
return (
<PostsContext.Provider value={posts}>
<Children />
</PostsContext.Provider>
)
}
// 2. user와 posts를 가져와 화면에 보여주기
const Children = () => {
const user = useContext(AppContext)
const posts = useContext(PostsContext)
let label = 'user'
if (user.isAdmin) {
label = 'admin'
}
return (
<div>
<div>{label}</div>
<div>{user.nickname}</div>
<div>{posts.map((post, index) => (
<div key={index}>
<div>{post.title}</div>
<div>{post.content}</div>
</div>
))}</div>
</div>
)
}
useContext를 이용하면 Context.Consumer로 컴포넌트를 작성할 때 보다 더 쉽고 편하고 직관적으로 작성할 수 있습니다. useState, useEffect 등 여러 React Hooks와 조합해서 사용하기에도 용이하다는 장점도 얻을 수 있습니다.
import React, { createContext, useContext } from 'react'
const Context = createContext()
// ...
const Children = () => {
const context0 = useContext(Context.Provider) // ERROR!!!
const context1 = useContext(Context.Consumer) // ERROR!!!
const context2 = useContext(Context) // OK
// ...
}
useContext는 createContext 함수를 실행한 결과를 그대로 파라미터로 넘겨줘야 합니다.
import React, { createContext } from 'react'
const AppContext = createContext()
// ...
const User = () => {
const appContext = useContext(AppContext)
// ...
}
이 예시와 같이 사용하다 보면 여러 Context를 모두 인지하고 있어야 하고, Context를 전달하기 전에 추가적인 처리를 해주고자 한다면 유연성이 부족하다는 이슈가 있습니다.
import React, { createContext, useContext, useMemo } from 'react'
const AppContext = createContext()
const useAppContext = () => useContext(AppContext)
// ...
const User = () => {
const appContext = useAppContext()
// ...
}
이 예시와 같이 유의미한 이름을 지정하고 사용하면 Context를 인지할 필요 없이 Custom Hooks를 사용하는 것처럼 작성할 수 있으며, 코드를 읽기도 한결 더 수월합니다.
좋은 강의 감사합니다.
마지막 소스 적용한 전체 소스가 궁금해서
두 번째 소스를 바탕으로 테스트 페이지 만들었습니다.
https://codesandbox.io/s/inspiring-forest-6f6lq
소스 파일을 두 개로 분기 했고 두번째 소스의 사소한 버그 하나 수정했습니다.
좋아요와 댓글 감사합니다.
오탈자, 질문 등은 언제든지 댓글로 달아주세요!