웹개발

[React] React Context API로 상태 공유하기

Heeyeon Choi 2024. 9. 9. 16:06
728x90

1.1 Context 설정하기

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

// NotificationContext 생성
interface NotificationContextType {
  hasNoti: boolean;
  setHasNoti: (value: boolean) => void;
}

const NotificationContext = createContext<NotificationContextType | undefined>(undefined);

interface NotificationProviderProps {
  children: ReactNode; // children 타입을 명시적으로 정의
}

// NotificationProvider 컴포넌트
export const NotificationProvider: React.FC<NotificationProviderProps> = ({ children }) => {
  const [hasNoti, setHasNoti] = useState<boolean>(false);

  return (
    <NotificationContext.Provider value={{ hasNoti, setHasNoti }}>
      {children}
    </NotificationContext.Provider>
  );
};

// Context를 사용하는 훅
export const useNotification = () => {
  const context = useContext(NotificationContext);
  if (!context) {
    throw new Error('useNotification must be used within a NotificationProvider');
  }
  return context;
};
 

1.2 NotificationProvider로 전체 앱을 감싸기

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { NotificationProvider } from './context/NotificationContext'; // 방금 만든 파일 경로

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

1.3 Header에서 hasNoti 사용하기

이제 Header 컴포넌트에서 useNotification 훅을 사용하여 hasNoti 값을 접근하고 수정할 수 있습니다.

import React, { useState, useEffect, useRef } from 'react';
import { Link } from 'react-router-dom';
import './Header.css';
import mainCharacterImg from '../img/main_character.png';
import UserProfile from './UserProfile';
import no_notification from '../img/no_notification.png';
import has_notification from '../img/has_notification.png';
import Notification from '../main/Notification';
import { useNotification } from '../context/NotificationContext';

interface ProfileProps {
  pageType: 'profileSetting' | 'signup' | 'logout' | 'otherblog' | 'myBlog';
}

const Header: React.FC<ProfileProps> = ({ pageType }) => {
  const notificationButtonRef = useRef<HTMLDivElement>(null); // 알림 버튼 참조
  const { hasNoti, setHasNoti } = useNotification(); // 전역 상태 사용
  const [nickname, setNickname] = useState<string>();
  const [message, setMessage] = useState<string>('');
  const [image, setImage] = useState<string>(mainCharacterImg);
  const [isImageLoaded, setIsImageLoaded] = useState<boolean>(false);
  const [token, setToken] = useState<string>('');
  const [openNotification, setOpenNotification] = useState<boolean>(false);

  const closeModal = () => {
    setOpenNotification(false);
    // 알림을 다 확인한 경우에만 false로 변경
    if (/* 조건 */) {
      setHasNoti(false);
    }
  };

  useEffect(() => {
    // 세션스토리지에서 닉네임과 토큰 가져오기
    try {
      const localStorageToken = sessionStorage.getItem('accessToken');
      if (localStorageToken === null) {
        setToken('');
      } else {
        setToken(localStorageToken);
      }
      const storedNickname = sessionStorage.getItem('nickname');
      if (storedNickname) {
        setNickname(storedNickname);
        setMessage(sessionStorage.getItem('message'));
        setImage(sessionStorage.getItem('image') || mainCharacterImg);
      }
    } catch (err) {
      console.log(err);
    }
  }, []);

  const profileImage = isImageLoaded ? image : mainCharacterImg;

  return (
    <header className="header">
      {/* Header 구조 */}
      <div className="header__auth">
        <div ref={notificationButtonRef} onClick={() => setOpenNotification(true)}>
          {openNotification && notificationButtonRef.current && (
            <Notification onClose={closeModal} buttonRef={notificationButtonRef} />
          )}
          {hasNoti ? (
            <img src={has_notification} style={{ width: '20px', height: 'auto', marginRight: '20px' }} />
          ) : (
            <img src={no_notification} style={{ width: '20px', height: 'auto', marginRight: '20px' }} />
          )}
        </div>
        {profileImage && <UserProfile profileType={'logout'} profileImage={profileImage}></UserProfile>}
      </div>
    </header>
  );
};

export default Header;

요약:

  • Context API를 통해 hasNoti 상태를 전역적으로 관리할 수 있습니다.
  • NotificationContext를 만들어 상태를 저장하고, useNotification 훅을 통해 컴포넌트에서 상태를 가져와 사용할 수 있습니다.
  • NotificationProvider로 애플리케이션을 감싸야 hasNoti 값을 전역에서 공유할 수 있습니다.
728x90