Javascript/React

useContext

kyoulho 2024. 7. 21. 14:27

컨텍스트란?

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

리액트는 이런 속성 전달의 번거로움을 해소하고자 컨텍스트라는 메커니즘을 구현해놓았다. 리액트나 리액트 네이티브에서 컨텍스트는 createContext와 useContext 훅으로 이뤄진다.

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

 

createContext

컨텍스트 기능을 구현하려면 createContext 함수로 컨텍스트 객체를 생성해야 한다.

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

const SomeContext = createContext<ContextType>(defaultContextValue)

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

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

Provider 컴포넌트는 다음처럼 value와 children 속성이 있는 ProviderProps 속성을 제공한다.

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

useContext

useContext 훅은 컨텍스트 객체가 제공하는 Provider 컴포넌트의 value 속성값을 얻을 수 있게 하는 목적으로 사용되는 훅이다.  이 값은 가장 가까운 상위 Context.Provider에서 제공된 값입니다. useContext는 항상 컨텍스트 제공자의 value 속성값을 반환한다.

// useContext 함수 선언
function useContext<T>(context: Context<T>): T;

// Context 인터페이스 선언
interface Context<T> {
        Provider: Provider<T>;
        Consumer: Consumer<T>;
        displayName?: string | undefined;
    }

예제

import type {FC, PropsWithChildren} from "react";
import {createContext, useContext} from "react";
import {useWindowResize} from "../hooks/useWindowResize";

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()
        const breakpoint = width < 640 ? 'sm' :
                           width < 768 ? 'md' :
                           width < 1024 ? 'lg' :
                           width < 1280 ? 'xl' : '2xml'
        const value = {breakpoint}

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

export const useResponsive = () => {
    const {breakpoint} = useContext(ResponsiveContext)
    return breakpoint
}
import {useResponsive} from "../contexts";
import {Subtitle, Title} from "../components";

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>
    )
}
import React from 'react'
import './App.css'
import ResponsiveContextTest from "./pages/ResponsiveContextTest";
import {ResponsiveProvider} from "./contexts";


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

 

useContext와 상태 관리 라이브러리

때로는 두 도구를 함께 사용하는 것이 유리할 수 있다. 예를 들어, Redux는 전역 상태 관리에 사용하고, useContext는 특정한 하위 트리에서 상태를 공유하는 데 사용할 수 있다. 이렇게 하면 상태 관리가 좀 더 명확해지고, 필요에 따라 적절한 도구를 사용할 수 있다.

728x90