본문 바로가기

FrontEnd/React

[React] useContext 훅 이해하기

컨텍스트란?

 

 컨텍스트를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있다.

리액트 컴포넌트의 데이터는 위에서 아래로(즉, 부모로부터 자식에게) props를 통해 전달하지만, 애플리케이션 안의 여러 컴포넌트들에 전해줘야 하는 props의 경우 이 과정이 번거로울 수 있다.

 

 context를 이용 시 트리 단계마다 명시적으로 props를 념겨주지 않아도 많은 컴포넌트가 값을 공유할 수 있으며 createContext와 useContext 훅으로 이뤄진다.

 

 컨텍스트 기능을 사용하는 리액트와 리액트 네이티브 코드는 항상 이름에 'Provider'가 있는 컴포넌트와 'use컨텍스트_이름()' 형태의 커스텀 훅을 사용한다.  

 

 컨텍스트 기능을 구현한 react-native-paper와 같은 패키지 또한 항상 Provider란 이름이 있는 컴포넌트와 Provider가 제공하는 정보를 사용할 수 있게 하는 useTime과 같은 커스텀 훅을 제공한다.

 

 

createContext 함수 탐구

 컨텍스트 기능을 구현 시 react 패키지가 제공하는 createContext 함수로 컨텍스트 객체를 생성해야 하며 타입스크립트에서 사용해야 하는 함수 호출 코드 패턴은 아래와 같다.

import React, {createcontext} from 'react'

type contextType = {}  // 공유할 속성
const defaultcontextValue: ContextType = {}  // 공유할 데이터 속성 초기값
const SomeContext = createContext<ContextType>(defaultContextValue)

 

 

테일윈드CSS의 중단점 접두사 이해하기

 테일윈드CSS, 부트스트랩 등 대다수 CSS 프레임워크는 sm, md, lg, lx 등을 사용해 웹 화면의 크기를 표현할 수 있고 이를 CSS 프레임워크가 제공하는 반응형 디자인 기능을 사용할 때 쓴다.

키워드 의미 최소 dk크기 CSS @media 규칙
sm small 640px @media (min-width: 640px)
md medium 768px @media (min-width: 768px)
lg large 1024px @media (min-width: 1024px)
xl extra large 1280px @media (min-width: 1280px)
2xl 2 extra large 1536px @media (min-width: 1536px)

 

// 중단점 접두사 사용 예
<img className="w-16 md:w-32 lg:w-48 />

 

 

반응형 컨텍스트 만들기

 반응형 컨텍스트를 만들기 위한 반응형 컨텍스트 ResponsiveContext 코드를 추가한다.

import {createContext} from 'react'

type ContextType = {
  breakpoint: string // 공유할 데이터 속성
}
const defaultContextValue: ContextType = {
  breakpoint: '' // 공유할 데이터 속성 초기값
}
export const ResponsiveContext = createContext<ContextType>(defaultContextValue)

 

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

  createContext 함수 호출로 생성된 컨텍스트 객체는 Provider와 Consumer라는 컴포넌트를 제공한다.  Provider는 컨텍스트의 기능을 제공할 컴포넌트이고, Consumer는 Provider가 제공한 기능을 사용하고 싶은 클래스 컴포넌트이다.

 

  Provider 컴포넌트는 value와 children 속성이 있는 ProviderProps 속성을 제공한다.  여기서 타입 변수 T는 createContext<T>와 같아야 하고, 자식 요소를 포함할 수 있는 컴포넌트에서만 사용이 가능한 children이 있다.  그리고 value속성에 설정하는 값이 Provider 컨텍스트가 제공하는 기능이다.

 

 

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

 

 

 ResponsiveContext를 App.tsx에 적용 후 발생하는 오류를 확인할 수 있다.  Provider 컴포넌트는 반드시 value 속성 값을 설정해줘야 하는데 해당 속성이 없어 발생하는 오류이다.  오류 해소를 위해 ResponsiveContext.Provider 안에 ResponsiveProvider 컴포넌트를 ResponsiveContext에 추가하자.

Provider의 value 속성값을 설정하지 않은 상태에서 App.tsx에 적용 후 발생하는 오류

 

 

 

 ResponsiveProvider 컴포넌트 만들기

  ResponsiveContext.Provider를 감싸는 responsiveProvider 컴포넌트의 초기 구현을 해보자.  ResponsiveContext.Provider는 value와 children 속성을 제공한다는 것을 전제조건으로 코드를 추가하자.

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

type ContextType = {
  breakpoint: string // 공유할 데이터 속성
}
const defaultContextValue: ContextType = {
  breakpoint: '' // 공유할 데이터 속성 초기값
}
export const ResponsiveContext = createContext<ContextType>(defaultContextValue)

type ResponsiveProviderProps = {}
export const ResponsiveProvider: FC<PropsWithChildren<ResponsiveProviderProps>> = ({
  children,
  ...props
}) => {
  const breakpoint = 'sm'
  const value = {
    breakpoint
  }
  return <ResponsiveContext.Provider value={value} children={children} />
}

 

 

  ResponsiveProvider 컴포넌트는 모든 컨텍스트 제공자의 가장 최상위 컴포넌트로 동작해야 하기 때문에 App.tsx의 <main>의 부모컴포넌트로 추가하자.

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

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

 

 

useContext 훅 알아보기

 useContext 훅은 컨텍스트 객체가 제공하는 Provider 컴포넌트의 value 속성값을 얻기 위해 사용하는 훅이다.  usecontext는 항상 컨텍스트 제공자의 value 속성값을 반환하도록 되어있다.

import {useContext} from 'react'
export const useResponsive = () => {
    const value = usecontext(ResponsiveContext)
    return value.breakpoint
}

 

  useContext를 ResponsiveContext에 코드를 추가하여 useContext를 통해 값을 얻어오도록 하자.

import type {FC, PropsWithChildren} from 'react'
import {createContext, useContext} from 'react'
import {useWindowResize} from '../hook'

type ContextType = {
  breakpoint: string // 공유할 데이터 속성
}
const defaultContextValue: ContextType = {
  breakpoint: '' // 공유할 데이터 속성 초기값
}
export const ResponsiveContext = createContext<ContextType>(defaultContextValue)

type ResponsiveProviderProps = {}
export const ResponsiveProvider: FC<PropsWithChildren<ResponsiveProviderProps>> = ({
  children,
  ...props
}) => {
  const [width] = useWindowResize()

  //prettier-ignore
  const breakpoint = width < 640 ? 'sm' : width <768 ? 'md' : width < 1024 ? 'lg'
                 : width < 1280 ? 'xl' : '2xl'

  const value = {
    breakpoint
  }
  return <ResponsiveContext.Provider value={value} children={children} />
}

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

 

  마지막으로 useResponsive 커스텀 훅을 적용한 결과물을 확인하기 위해 코드를 추가한다.  이를 통해 최상위 부모 컴포넌트가 컨텍스트 제공자 컴포넌트를 통해 제공하는 기능을 자식 컴포넌트들에게 useContext 훅으로 사용할 수 있게 하는 기능인 것을 확인할 수 있다.

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

export default function ResponsiveContextTest() {
  const breakpoint = useResponsive()
  return (
    <section className="mt-4">
      <Title>ResponsiveContextTest</Title>
      <div className="mt-4">
        <Subtitle>breakpoint : {breakpoint}</Subtitle>
      </div>
    </section>
  )
}

 

실행결과 : 브라우저 창 크기마다 breakpoint가 변화하는 것을 확인할 수 있다.