본문 바로가기
📌 Front End/└ React

[React] 리액트 훅 - useContext(전역 상태값 공유)

by 쫄리_ 2024. 8. 29.
728x90
반응형

📌 useContext 훅

React 컴포넌트는 부모에서 자식으로 props를 통해 데이터를 전달해야한다.
이때, 컴포넌트의 수가 많아지고 트리 구조가 복잡해지면 
단계별로 데이터를 전달해야하는 리액트에서 코드는 굉장히 복잡해지기 마련이다.
중간에 코드가 바뀌게 된다면 일일히 다 찾아 바꿔야하기도 하고,
해당 데이터가 필요하지 않은 컴포넌트에도 데이터가 전달되어 코드가 지저분해지기도 한다.
 
이런 문제를 해결하기 위해 우리는 useContext 를 사용한다.
전역적으로 사용되는 데이터들을 공유해

최상단 컴포넌트에서 최하단 컴포넌트까지 데이터 전달을 손쉽게 할 수 있다.
ex) 유저 정보, 테마, 언어, ...


 

useContext() 훅은 리액트에서 상태를 전역적(globally)으로 사용할 수 있도록 해줍니다. 

useState()로 생성한 상태는 기본적으로 지역적(locally)이지만, 

useContext()와 함께 사용하면 상태를 전역적으로 사용할 수 있어집니다.

 

🔎 useState만 사용해서 앱 컴포넌트에 전체에 theme 상태를 공유하려고 하면 

      모든 컴포넌트마다 props로 theme 상태를 지정해 내려줘야하는 큰 불편함이 생기게 됩니다.

 

🔎 useContext와 함께

      useState를 사용하면 상태를 전역적으로 관리하므로

      필요한 곳에서만 useContext 함수로 호출해서 사용

 

 


📌 useContext 구성요소

  • 컨텍스트 생성(전역공유) ➡️ createContext : 컨텍스트 객체 생성
  • 부모 컴포넌트 ➡️ Provider : 생성한 컨텍스트를 하위 컴포넌트에게 전달
  • 자식 컴포넌트 ➡️ useContext(Context); : 해당 컨텍스트의 현재 상태값 가져옴

 


✅ createContext() - 컨텍스트 생성 (전역 공유)

  • React.createContext(defaultValue);
  • 컨텍스트 객체를 생성하여 전역 상태를 관리
  • createContext() 함수 호출 시 Provider와 Consumer 반환
    • Provider : 생성한 context를 하위 컴포넌트에게 전달하는 역할
    • Consumer : context의 변화를 감시하는 컴포넌트
  • 초기값 : Provider를 사용하지 않았을 때 적용될 초기값
import React, { createContext } from 'react';
export const UserContext = React.createContext(초기값);

✅ Provider - 컨텍스트 값 제공, 하위 컴포넌트에게 값 전달

  • Context.Provider
    컨텍스트의 값을 제공하고, 하위 컴포넌트들에게 해당 값 전달
    Provider로 감싸진 모든 하위 컴포넌트에서 사용가능 (전역 공유)
  • value={prop} 
    Provider의 value prop을 통해 컨텍스트에 전달할 데이터를 설정
  • 객체 형식의 데이터는 value={{data}} 형태로 전달
    - 첫 번째 {} : JSX에서 자바스크립트 표현식을 사용
    - 두 번째 {} : 자바스크립트 객체를 만들기 위한 중괄호
<UserContext.Provider value={ 공유할 데이터 } />
   <하위컴포넌트 />
</UserContext.Provider>

✅ useContext(Context) - 현재 상태값 가져오기

  • 컨텍스트의 현재 상태값을 가져온다.
  • createContext를 통해 생성된 Context 객체를 파라미터로 넘겨받는다.
  • 이 객체는 Context.Provider를 통해 제공된 값을 읽어옵니다.
import React, { useContext } from 'react';
import { UserContext } from './context'; // 컨텍스트 객체 가져오기

const ChildComponent: React.FC = () => {
  const context = useContext(UserContext);

  if (context === undefined) {
    throw new Error('ChildComponent must be used within a UserProvider');
  }

  return (
    <div>
      // 컨텍스트에서 가져온 값 출력
      유저 아이디 : {context.userID}
    </div> 
  );
};

export default ChildComponent;

 


✏️ useContext 사용해보기

📜 context.tsx  -  컨텍스트 제공자 (UserProvider) 정의

  • 역할 : 컨텍스트와 그에 관련된 타입, 초기값, Provider 정의
  • 내용
    • 컨텍스트 객체 생성 (createContext)
    • Provider 컴포넌트 정의
UserProviderProps UserProvider 컴포넌트의 props 타입 정의

children : ReactNode;
UserProvider가 받는 children 프로퍼티의 타입을 정의합니다.
ReactNode는 모든 리액트 요소(자식 컴포넌트, 텍스트, 프래그먼트 등)를
포함하는 타입입니다.
FC<UserProviderProps> FC (Functional Component)
리액트의 함수형 컴포넌트 타입 정의
React.FC와 FC는 동일한 타입
React.FC<UserProviderProps>

FC<UserProviderProps> 타입을 사용하면
UserProvider가
UserProviderProps 타입의 props를 받는 함수형 컴포넌트로 정의
{children} UserProvider가 자신의 자식 컴포넌트를 렌더링합니다. 
Provider 내부에서 {children}은 UserProvider로 감싸진 
모든 하위 컴포넌트를 렌더링하는 역할을 합니다.

 

import React, { createContext, useState, ReactNode, FC } from 'react';

// ▶ 컨텍스트 타입 정의
export interface UserContextType {
  userID: string;
  setUserID: (id: string) => void; // setUserID 함수의 타입 정의
}

// ▶ UserContext 컨텍스트 생성(초기값 undefined)
// → 자식 컴포넌트에서 useContext(UserContext)를 호출하면, 
//   UserContext.Provider가 제공하는 value 객체를 사용할 수 있습니다.
export const UserContext = createContext<UserContextType | undefined>(undefined);

// ▶ UserProviderProps: props의 구조를 정의
interface UserProviderProps {
  // Provider의 자식 요소의 타입 정의(ReactNode)
  children: ReactNode; // 리액트 노드: JSX 요소, 문자열 등 렌더링 가능한 모든 요소
}

// ▶ UserProvider 컴포넌트
// → 컨텍스트를 정의하고, 상태를 설정하며, 상태를 자식 컴포넌트에 제공
// → userID와 setUserID를 상태 가지고 있다.
//   UserContext.Provider를 통해 전달
// → UserProvider: FC<UserProviderProps> 타입의 함수형 컴포넌트
export const UserProvider: FC<UserProviderProps> = ({ children }) => {
  // ● 상태 초기값 설정
  // → 상태 정의: userID와 이를 변경하는 함수
  const [userID, setUserID] = useState<string>('');

  // ● 컨텍스트에서 제공할 값
  // → userID: useState로 정의된 상태 변수의 현재 값
  // → setUserID: useState로 정의된 상태를 변경하는 함수
  const value: UserContextType = { userID, setUserID };

  // ● 컨텍스트 제공
  // → UserContext.Provider를 사용하여, 상태와 상태 업데이트 함수를 value로 설정
  //   UserProvider로 감싼 자식 컴포넌트들은 이 value를 사용할 수 있다.
  return (
    <UserContext.Provider value={value}>
      {children} {/* 전달받은 자식 요소들을 렌더링 */}
    </UserContext.Provider>
  );
};

 

📜 App.tsx  -  루트 컴포넌트 정의하고 렌더링

  • 역할
    애플리케이션의 루트 컴포넌트를 정의하고, 
    Provider로 애플리케이션을 감싸서 하위 컴포넌트들에게 컨텍스트를 제공하는 역할

  • 내용
    • Provider를 사용하여 애플리케이션을 감쌈
    • 하위 컴포넌트를 렌더링
<UserProvider>
· · ·
</UserProvider>
UserProvider로 애플리케이션을 감싸 
ChildComponent와 같은 하위 컴포넌트들이 
컨텍스트 값을 사용할 수 있도록 합니다.
import React from 'react';
import ReactDOM from 'react-dom';
import { UserProvider } from './context';	// UserProvider 가져오기
import ChildComponent from './ChildComponent';	// useContext를 사용하는 컴포넌트

// ▶ App: UserProvider를 사용하여 자식 컴포넌트를 전달하는 컴포넌트
export const App: React.FC = () => {
  return (
    <UserProvider>
      <ChildComponent /> {/* UserProvider로 감싸서 자식 컴포넌트가 컨텍스트를 사용 가능 */}
    </UserProvider>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));

 

📜 ChildComponent.tsx  -  컨텍스트 데이터를 사용하는 컴포넌트

 

  • 역할 : useContext를 사용하여 컨텍스트 값을 가져오고 이를 화면에 출력
  • 내용
    • useContext를 통해 컨텍스트 값 읽기
    • 값을 화면에 표시
import React, { useContext } from 'react';
import { UserContext } from './context';	// 컨텍스트 객체 가져오기

const ChildComponent: React.FC = () => {
  // ▶ useContext를 사용하여 컨텍스트 값을 가져옴
  // → useContext(UserContext)는 현재 UserContext.Provider가 제공한 값을 반환
  //   자식 컴포넌트는 useContext(UserContext)를 통해, 
  //   UserProvider에서 제공한 value를 가져옵니다.
  // → value에는 상태(userID)와 상태를 변경하는 함수(setUserID)가 포함
  //   자식 컴포넌트는 이 값을 사용하여 상태를 읽고, 변경할 수 있다.
  const context = useContext(UserContext);

  // ▶ 컨텍스트가 제공되지 않았다면 에러 발생
  if (context === undefined) {
    throw new Error('ChildComponent must be used within a UserProvider');
  }

  // ▶ userID를 변경하는 함수
  const changeUserID = () => {
    context.setUserID('소현'); // 예시로 '소현'으로 변경
  };

  return (
    <div>
      유저 아이디 : {context.userID}  {/* 컨텍스트에서 가져온 값 출력 */}
      <button onClick={changeUserID}>Change User ID to '소현'</button> {/* 버튼 클릭 시 userID 변경 */}
    </div>
  );
};

export default ChildComponent;

 


 

728x90
반응형