Context API로 modal 관리하기

곽재훈·2024년 8월 28일
0

React나 Next.js의 프로젝트에서 모달을 관리하는 가장 간단한 방법은 페이지 내에서 state로 modal의 활성 여부를 관리하는 것이다.

// App.tsx

const App = () => {
  const [isModalOpen, setModalOpen] = useState<boolean>(false)
  
  return <div>
    {isModalOpen && <Modal/>}
    ...페이지 본문
  </div>
}

export default App;

다만 위와 같이 사용하면 페이지마다 state를 추가해서 관리해줘야 하는 번거로움이 있다. 그래서 최종 프로젝트에서는 modal을 페이지 단위가 아니라 프로젝트 차원에서 관리할 수 있도록 Context API를 활용하여 만들기로 했다.

// Modal.context.tsx
interface TInitialValue {
  alert: (contents: string[]) => void;
  confirm: (contents: string[]) => void;
  close: () => void;
}

const initialValue: TInitialValue = {
  alert: () => {},
  confirm: () => {},
  close: () => {},
};
const ModalContext = createContext<TInitialValue>(initialValue);

export const useModal = () => useContext<TInitialValue>(ModalContext);

export function ModalProvider({ children }: PropsWithChildren) {
  const [modal, setModal] = useState<React.ReactNode | null>(null);


  const value = {
    alert: (contents: string[]) => {
      setModal(<AlertModal contents={contents} />)
    },
    confirm: (contents: string[]) => {
	  setModal(<ConfirmModal contents={contents} />)
    },
    close: () => {
      setModal(null);
    },
  };

  return (
    <ModalContext.Provider value={value}>
      {children}
      {modal}
    </ModalContext.Provider>
  );
}

Context API를 활용해서 Provider를 만들어 modal을 관리해보자.

1. initialValue 설정

context를 만들기 위해서는 초기값이 필요하다. 다만 주로 component 내에서 다시 선언하여 provider의 value로 전달하기 때문에 주로 빈 값들이 들어가는 듯 하다. 하지만 type은 정확히 맞춰주기!

interface TInitialValue {
  alert: (contents: string[]) => void;
  confirm: (contents: string[]) => void;
  close: () => void;
}

const initialValue: TInitialValue = {
  alert: () => {},
  confirm: () => {},
  close: () => {},
};

2. createContext로 context 만들기

createContext 함수에 인자로 앞에서 선언한 초기값인 initialValue를 전달한다.
generic으로 initialValue의 type도 전달!

const ModalContext = createContext<TInitialValue>(initialValue);

3. (부록) context 호출 부분을 hook으로 분리하기

원래는 context를 생성한 뒤에 context를 사용하는 페이지로 이동해서 아래와 같이 호출해야한다.

// App.tsx

import ModalContext from "..."

const App = () => {
  const modal = useContext<TInitialValue>(ModalContext)
  const openModal = () => {
  	modal.alert(['게시글 등록에 성공했습니다.'])
  }
  ...
}

export default App;

이렇게 매번 useContext 함수의 인자로 ModalContext를 전달해서 context를 불러와야 했다. 그러나 이 부분을 사전에 hook으로 분리하면 context 호출부를 보다 간단하게 바꿀 수 있다.

// Modal.context.tsx

...

export const useModal = () => useContext<TInitialValue>(ModalContext);

...

// App.tsx

import useModal from "..."

const App = () => {
  const modal = useModal();
  const openModal = () => {
  	modal.alert(['게시글 등록에 성공했습니다.'])
  }
  ...
}

export default App;

위와 같이 useModal이라는 hook으로 사전에 분리하면 component 내에서 context를 호출할 때 코드가 훨씬 간단해지는 것을 볼 수 있다.

4. Modal을 담을 Provider Component 생성

export function ModalProvider({ children }: PropsWithChildren) {
  const [modal, setModal] = useState<React.ReactNode | null>(null);
  ...
}

modal을 담을 Provider는 일반적으로 프로젝트의 가장 상위 부분에서 프로젝트를 감싸기 때문에 children을 받아서 modal과 함께 내려주도록 한다.

5. Provider에 전달할 value 설정

const value = {
    alert: (contents: string[]) => {
      setModal(<AlertModal contents={contents} />)
    },
    confirm: (contents: string[]) => {
	  setModal(<ConfirmModal contents={contents} />)
    },
    close: () => {
      setModal(null);
    },
  };

context를 생성할 때 인자로 전달했던 initialValue와 같은 형태지만, 이번에는 실제로 Provider에 전달할 value를 설정한다.

보통 modal을 관리하는 state를 조정하는 method들이 들어간다.
나의 경우에는 alertconfirm modal을 각각 분리해서 구현했고, modal을 닫을 때는 close라는 method로 state를 비워줬다.

6. Provider에 value 전달

{
...
return (
    <ModalContext.Provider value={value}>
      {children}
      {modal}
    </ModalContext.Provider>
  );
}

Provider component의 return 부분.
앞에서 생성한 modalContext에서 Provider를 사용하여 위에서 만든 value를 props로 전달.
modal이 활성화되면 보일 수 있도록 modal을 추가.

7. (중요) Provider를 상위 경로에 추가

프로젝트를 생성하는 방법에 따라 다르겠지만 next.js라면 가장 상단에 있는 layout.tsx가 될 것이고, vite로 react project를 만들었다면 main.tsx가 될 것인데, 우리가 만들 Provider component가 프로젝트 전체를 감싸도록 component를 추가해줘야 한다.

경우에 따라서는 프로젝트 전체가 아니라 modal이 사용되는 경로에 한정하여 Context API의 Provider가 씌워질 수도 있지만, 그렇게 구현할 정도라면 이미 설명이 필요하지 않을 정도의 이해도가 갖춰졌을 것이라고 생각한다.

...
<ModalProvider>
  <App/>
</ModalProvider>
...

혹은

...
<ModalProvider>
  {children}
</ModalProvider>
}
...

위와 같은 형태로 프로젝트 상위 경로에서 Provider를 활용해 감싸주면 modal을 사용할 준비 완성이다!

profile
개발하고 싶은 국문과 머시기

0개의 댓글

관련 채용 정보