전역 상태 라이브러리인 zustand를 활용해서 커스텀 Alert을 클라이언트 사이드 어디서든 띄울 수 있도록 구현했다.
queue
에 담아서 관리하도록 했다. (한번에 여러 alert이 뜨게 될 수도 있으니깐!)push
), 삭제하는(pop
) 로직을 넣어줬다.id
값을 넣어줬다.import { create } from 'zustand';
type AlertType = 'error' | 'success' | 'info';
export interface AlertContent {
type: AlertType;
message: string;
persistent?: boolean;
}
export interface AlertData extends AlertContent {
id: number;
}
export type AlertStore = {
nextId: number;
queue: AlertData[];
push: (content: AlertContent) => void;
pop: (id: number) => void;
};
const useAlertStore = create<AlertStore>()((set) => ({
nextId: 0,
queue: [],
push: (content) => {
set((state) => ({
nextId: state.nextId + 1,
queue: [...state.queue, { id: state.nextId, ...content }],
}));
},
pop: (id) => {
set((state) => ({
queue: state.queue.filter((alert) => alert.id !== id),
}));
},
}));
export default useAlertStore;
daisyui
와 heroicons
을 사용했다.persistent
설정이 있으면, x 버튼을 눌러야 사라지고,function Alert() {
const { queue, pop } = useAlertStore((state) => ({
queue: state.queue,
pop: state.pop,
}));
return (
<div className='z-999 toast toast-end items-end'>
{queue.map(({ id, type, message, persistent }) => (
<AlertItem key={id} type={type} message={message} persistent={persistent} onClose={() => pop(id)} />
))}
</div>
);
}
interface AlertItemProps {
type: 'error' | 'success' | 'info';
message: string;
persistent?: boolean;
onClose: () => void;
}
function AlertItem({ type, message, persistent, onClose }: AlertItemProps) {
useEffect(() => {
if (!persistent) {
setTimeout(() => {
onClose();
}, 3000);
}
});
return (
<div
className={cn('alert w-fit min-w-400 text-14', {
'alert-error': type === 'error',
'alert-success': type === 'success',
'alert-info': type === 'info',
})}
>
{type === 'error' && <XCircleIcon className='h-24 w-24' />}
{type === 'success' && <CheckIcon className='h-24 w-24' />}
{type === 'info' && <InformationCircleIcon className='h-24 w-24' />}
<span className='ml-2'>{message}</span>
{persistent && <XMarkIcon className='h-18 w-18 cursor-pointer' onClick={onClose} />}
</div>
);
}
export default Alert;
Alert
컴포넌트를 넣어준다.export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang='ko'>
<body>
{children}
<Alert />
</body>
</html>
);
}
function alert({ type, message, persistent }: AlertContent) {
useAlertStore.getState().push({ type, message, persistent });
}
export default alert;
요렇게 에러 처리를 할 수도 있다.
function someFunction() {
try {
...something
} catch (error) {
alert({ type: 'error', message: error.message });
}
}