useEffect(()=>{
return () => {
clearState()
}
},[clearState])
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: '약관 동의가 필요합니다.',
});
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',
});