전역 상태 관리 적용 사례

Kyung·2023년 5월 2일
0

전역 상태 관리 대상

  1. Alert
  2. Modal
  3. Loading
  4. Theme
  5. 세번 이상 props를 통해 전달해야하는 상태 ex) 2 step form
    • 해당 경우, 더이상 state를 사용하지 않는 경우, useEffect의 clean up function을 통해 상태를 clear시켜주어야 버그 발생 위험을 없앨 수 있다.
useEffect(()=>{
	return () => {
		clearState()
	}
},[clearState])

Alert

atoms/alert.tsx

import { atom } from 'jotai';

type AlertStateType = {
  Component: ((props: any) => JSX.Element) | null;
  props: { [key: string]: any };
  responseHandler: (value: boolean | PromiseLike<boolean>) => void;
};

export const alertAtom = atom<AlertStateType>({
  Component: null,
  props: {},
  responseHandler: () => null,
});

export const closeAlertAtom = atom(null, (get, set, state: boolean) => {
  const { responseHandler } = get(alertAtom);
  responseHandler(state);
  set(alertAtom, {
    Component: null,
    props: {},
    responseHandler: () => {},
  });
});

components/Alert.tsx

import { useAtomValue, useSetAtom } from 'jotai';

import { alertAtom, closeAlertAtom } from '@/atoms/alert';

export type AlertProps = {
  onClose: (value: boolean) => void;
};

const Alert = () => {
  const { Component, props } = useAtomValue(alertAtom);
  const close = useSetAtom(closeAlertAtom);

  const onClose = (value: boolean) => {
    close(value);
  };

  return (
      {Component && (
        <motion.div
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{
            opacity: 0,
          }}>
          <Component {...props} onClose={onClose} />
        </motion.div>
      )}
  );
};

export default Alert;

ConfirmAlert.tsx

const ConfirmAlert = ({
  titleText,
  infoText,
  onConfirm = () => {},
  onCancel = () => {},
  onClose,
}: ConfirmAlertProps) => {
  const handleConfirm = async () => {
    await onConfirm();
    onClose(true);
  };

  const handleCancel = () => {
    onCancel();
    onClose(false);
  };

  return (
    <WrappingContainer>
      <Container>
        <Title>{titleText}</Title>
        <Info>{infoText}</Info>
        <ButtonContainer>
          <CancelButton onClick={handleCancel}>취소</CancelButton>
          <ConfirmButton onClick={handleConfirm}>확인</ConfirmButton>
        </ButtonContainer>
      </Container>
    </WrappingContainer>
  );
};

export default ConfirmAlert;

useAlert.tsx

import { useSetAtom } from 'jotai';

import { alertAtom } from '@/atoms/alert';

const useAlert = () => {
  const setAlertState = useSetAtom(alertAtom);

  const alert = async (
    Component: (props: any) => JSX.Element,
    props: { [key: string]: any },
  ) =>
    new Promise((resolve) => {
      setAlertState({
        Component,
        props,
        responseHandler: resolve,
      });
    });

  return { alert };
};

export default useAlert;

App.tsx

import { Alert } from '@/components';

const App = () => (
  <Container>
      <Something />
      <Alert />
  </Container>
);

export default App;

사용

const { alert } = useAlert();

const response = await alert(ConfirmAlert, {
        titleText: '약관에 동의하시겠습니까?',
        infoText: '약관 동의가 필요합니다.',
});

Multi Modal

  • 하나의 모달만이 아니라 하나의 화면에 여러 개의 모달이 동시에 나올 수 있도록 구현한다.

atoms/modals

import { atom } from 'jotai';

type ModalStateType = {
  Component: (props: any) => JSX.Element;
  props: { [key: string]: any };
  key: string;
};

export const modalsAtom = atom<ModalStateType[]>([]);
// 배열로 관리

export const openModalAtom = atom(null, (get, set, modal: ModalStateType) => {
  const modals = get(modalsAtom);
  set(modalsAtom, [...modals, modal]);
});

export const closeModalAtom = atom(null, (get, set, key: string) => {
  const modals = get(modalsAtom);
  set(
    modalsAtom,
    modals.filter((modal) => modal.key !== key),
  );
});

components/Modal.tsx

import { useAtomValue, useSetAtom } from 'jotai';

import { modalsAtom, closeModalAtom } from 'atoms/modals';

const Modals = () => {
  const openedModals = useAtomValue(modalsAtom);
  const close = useSetAtom(closeModalAtom);

  return (
    <>
      {openedModals.map((modal) => {
        const { Component, props, key } = modal;

        const onClose = () => {
          close(key);
        };

        return <Component {...props} key={key} onClose={onClose} />;
      })}
    </>
  );
};

export default Modals;

App.tsx

import { Modals } from '@/components';

const App = () => (
  <Container>
      <Something />
      <Modals />
  </Container>
);

export default App;

사용

const openModal = useSetAtom(openModalAtom);

openModal({
  Component: ModalComponent,
  props: {
	title: '타이틀',
	subTitle: '서브 타이틀',
  },
  key: 'modal-components',
});

참고 자료

profile
개발 일지를 작성합니다

0개의 댓글