관리 메뉴

bright jazz music

useContext 훅 본문

Framework/ReactJs

useContext 훅

bright jazz music 2023. 6. 16. 08:26

- 컨텍스트는 리액트가 제공하는 가장 막강한 기능

- 컨텍스트는 리액트 라우터, 리덕스 등 많은 라이브러리 설계의 근간이 됨.

 

컨텍스트란?

컴포넌트의 속성(props)은 부모 컴포넌트가 자식 컴포넌트로 어떤 정보를 전달하려고 할 때 사용하는 메커니즘이다. 그런데 부모 컴포넌트가 직계 자식이 아닌, 손자나 증손자 컴포넌트에 정보를 전달하려고 하면 아래와 같이 번거로운 props 전달을 해야 한다.

리액트는 이런 속성 전달의 번거로움을 해소하고자 context라는 메커니즘을 구현하였다.

리액트나 리액트 네이티브에서 컨텍스트는 createContext나 useContext로 이뤄진다. 

이 둘의 관계는 아래의 그림처럼 표현할 수 있다.

컨텍스트 기능을 사용하는 리액트와 리액티브 코드는 항상 이름에 'Provider'가 있는 컴포넌트와 'use컨텍스트이름()' 형태의 커스텀 훅을 사용한다. 컨텍스트 기능을 구현한 react-native-paper와 같은 패키지 또한 항상 Provider란 이름이 있는 컴포넌트와 Provider가 제공하는 정보를 사용할 수 있게 하는 useTheme과 같은 커스텀 훅을 제공한다.

 

createContext 함수 탐구

 

컨텍스트 기능을 구현할 때 가장 먼저 해야 할 일은 react 패키지가 제공하는 createContext 함수로 컨텍스트 함수로 컨텍스트 객체를 생성하는 것이다.

import React, {createContext} from 'react'

그런데 타입스크립트에서 createContext함수 호출은 다음과 같은 코드 패턴으로 작성해야 한다.

type ContextType = {
	//공유할 데이터 속성
}

const defaultContextValue: ContextType = {
	//공유할 데이터 속성 초깃값
}

const SomeContext = createContext<ContextType>(defaultContextValue)
	//우측의 createContext()를 사용하여 컨텍스트 객체를 만들어준다.
    // 이 객체는 Provider와 Consumer라는 컴포넌트를 제공한다.

 

 

참고

 

 

컨텍스트 객체가 제공하는 Provider 컴포넌트

createContext 함수 호출로 생성된 컨텍스트 객체는 Provider와 Consumer라는 컴포넌트를 제공한다.

- Provider는 컨텍스트의 기능을 제공할 컴포넌트이다.

- Consumer는 Provider가 제공한 기능을 사용하려는 클래스 컴포넌트이다. 

 

만약 컴포넌트를 함수형으로 구현한다면 Consumer는 무시해도 된다. 함수 컴포넌트는 useContext 훅을 사용하면 되기 때문이다. useContext는 클래스 컴포넌트와 달리 훨씬 용법이 간단하다.

 

Provider 컴포넌트는 value와 child 속성(props)가 있는 ProviderProps 속성을 제공한다. 

interface ProviderProps<T> {
	value: T;
	Children?: ReactNode;
}

//index.d.ts에서는
 // Context via RenderProps
interface ProviderProps<T> {
	value: T;
	children?: ReactNode | undefined;
}

여기서 타입 변수 T는 createContext<T>와 같아야 하고, Children은 02-4절에서 알아본 컴포넌트의 children 속성과 같다. 그리고 value 속성에 설정하는 값이 Provider 컨텍스트가 제공하는 기능이 된다.

 

Provider 컴포넌트는 반드시 value 속성에 값을 설정해 줘야 한다. 설정하지 않으면 오류가 발생한다.

 

예시: ResponsiveProvider 컴포넌트 만들기

// src/contexts/ResponsiveContext.tsx

//ResponsiveProvider 만들기

import type {FC, PropWithChildren} from 'react'
import {createContext} from 'react'

type ContextType = {
	breakPoint: string //공유할 데이터 속성
}

const defaultContextValue: ContextType = {
	breakpoint: '' //공유할 데이터 속성 초깃값
}

export const ResponsiveContext = createContext<ContextType>(defaultContextValue)

type ResponsiveProviderProps = {}

//ResponsiveProvider 만들기
export const ResponsiveProvider: FC<PropsWithChildren<ResponsiveProviderProps>> = ({
	children,
    ...props
}) => {

	const breakpoint = 'sm'
    const value = {
    	breakpoint //breakpoint: breakpoint 코드를 간결하게 표
    }
    
    return <ResponsiveContext.Provider value={value} children={children} />
}

ResponsiveProvider 컴포넌트를 App.tsx 파일에 적용하기. 

모든 컨텍스트 제공자는 가장 최상위 컴포넌트로 동작해야 한다는 원칙에 따라 ResponsiveProvider를 <main>의 부모 컴포넌트로 만들었다.

 

import ResponsiveContextTest from './pages/ResponsiveContextTest'
import {ResponsiveProvider} from './contexts'

export default function App() {
	return (
    	<ResponsiveProvider>
        	<main>
            	<ResponsiveContextTest />
            </main>
        </ResponsiveProvider>
        
    )
}

 

useContext 훅 알아보기

ResponsiveContextTest 컴포넌트를 구현하기 전에 그 전에 useContext 훅을 알아보자.

 

- useContext훅은 컨텍스트 객체가 제공하는 Provider 컴포넌트의 value 속성값을 얻을 수 있게 하는 목적으로 사용되는 훅이다.

 

useContext 훅을 사용하려면 다음처럼 임포트 해야 한다.

 

import {useContext} from 'react'

 

다음 코드는 useContext 훅을 사용하는 useResponsive 커스텀 훅을 구현한 예.

useContext는 항상 컨텍스트 제공자의 value 속성값을 반환하므로 앞서 본 컨텍스트 제공자의 value 속성에 설정해 놓았던 breakpoint 멤버 속성값을 반환한다.

export const useResponsive = () => {
	const value = useContext(ResponsiveContext)
    return value.breakpoint
}

 

다음 코드는 지금까지의 내용을 모두 종합한 ResponsiveContext.tsx 파일 구현 내용이다. 이전 장에서 제작한 useWindowResize 훅으로 현재 웹페이지의 넓이를 구한 뒤 앞에서 알아본 테일윈드 CSS의 중단점 접두사가 의미하는 픽셀 크기에 따라 breakpoint 변숫값을 계산한다. 또한 객체에 적용하는 비구조화 할당 구문으로 더 간결하게 구현하였다.

// src/contexts/ResponsiveContext.tsx

import type {FC, PropWithChildren} from 'react'
import {createContext} from 'react'
import {useWindowResize} from '../hooks'

type ContextType = {
	breakPoint: string //공유할 데이터 속성
}

const defaultContextValue: ContextType = {
	breakpoint: '' //공유할 데이터 속성 초깃값
}

export const ResponsiveContext = createContext<ContextType>(defaultContextValue)

type ResponsiveProviderProps = {}

export const ResponsiveProvider: FC<PropsWithChildren<ResponsiveProviderProps>> = ({
	children,
    ...props
}) => {
	//prettier-ignore
	const breakpoint = width < 640 ? 'sm' :
    					width < 768 ? 'md' :
                        width < 1024 ? 'lg' :
                        width < 1280 ? 'xl' : '2xl'
    const value = {
    	breakpoint //breakpoint: breakpoint 코드를 간결하게 표
    }
    
    return <ResponsiveContext.Provider value={value} children={children} />
}

	//	useResponsive 커스텀 훅
export const useResponsive = () = > {
	const {breakpoint} = useContxt(ResponsiveContext)
    return breakpoint
}

 

 

앞서 제작한 useResponsive 커스텀 훅을 src/pages 디렉토리의 ResponsiveContextTest.tsx 파일에 적용한다.

useResponsive 커스텀 훅은 항상 breakpoint 값을 반환하므로 다음처럼 구현할 수 있다.

import {Title, Subtitle} from '../components'
import {useResponsive} from '../contexts'

export default function ResponsiveContextTest() {
	
    //context값 가져와서 변수에 저장
	const breakpoint = useResponsive()
    
	return (
    	<section className="mt-4">
        	<Title>ResponsiveContextTest</Title>
            <div className="mt-4">
            	<Subtitle>breakpoint: {breakpoint}</Subtitle>
            </div>
       	</section>
    )
}

 

 

실행결과를 보면 현재 웹페이지의 넓이가 640px보다 작으므로 중단점이 sm으로 표시된다. 이처럼 컨텍스트는 최상위 부모 컴포넌트가 컨텍스트 제공자 컴포넌트를 통해 제공하는 기능을 자식뿐만 아니라 자손 컴포넌트들도 useContext  훅으로 사용할 수 있게 하는 기능이다. 리덕스, 리액트 라우터 등 대부분의 리액트 기반 패키지들은 이 컨텍스트 기능으로 구현되었다.

Comments