Recoil을 사용하여 실시간으로 알림 목록과 개수를 업데이트하는 기능을 구현해보려고 한다!
먼저, 알림 데이터와 알림 개수를 관리하기 위한 두 개의 atom을 생성한다.
// Atom.js
export const noticeListState = atom({ // 알림 목록 상태 관리
key: 'noticeListState',
default: [],
});
export const noticeCountState = atom({ // 알림 개수 상태 관리
key: 'noticeCountState',
default: 0,
});
useSocketManager
라는 커스텀 훅을 구현하여 이 훅 내에서 Recoil 상태를 설정하고, 알림 데이터를 처리한다.
// useSocketManager.jsx
import { useSetRecoilState } from 'recoil';
import { isConnectSocketState, noticeCountState, noticeListState } from '../recoil/Atom.js';
.
.
.
export function useSocketManager() {
const setIsConnectSocketState = useSetRecoilState(isConnectSocketState);
const setNoticeList = useSetRecoilState(noticeListState);
const setNoticeCount = useSetRecoilState(noticeCountState);
const getNotice = async () => {
if (!getLoginUserId()) return;
try {
const responseData = await fetchNotification();
if (responseData.success) {
setNoticeList(responseData.data); // 전체 알림 데이터
const newData = responseData.data.filter(
(notice) => notice['Participants.status'] === 0
);
setNoticeCount(newData.length); // 새로운 알림 데이터 개수
}
} catch (error) {
console.error(error);
}
};
useEffect(() => {
getNotice();
}, []);
.
.
.
}
const setNoticeList = useSetRecoilState(noticeListState);
const setNoticeCount = useSetRecoilState(noticeCountState);
→ useSetRecoilState
훅을 사용하여 상태를 설정할 수 있는 함수 noticeListState
, noticeCountState
를 가져온다.
const getNotice = async () => {
→ 전체 알림 데이터와 새로운 알림 데이터 개수를 설정할 함수를 선언한다.
const responseData = await fetchNotification();
→ fetchNotification()
함수를 호출하여 서버로부터 전체 알림 데이터를 가져와 응답을 처리한다.
setNoticeList(responseData.data); // 전체 알림 데이터
const newData = responseData.data.filter(
(notice) => notice['Participants.status'] === 0
);
setNoticeCount(newData.length); // 새로운 알림 데이터 개수
→ 응답으로 받은 전체 알림 데이터를 setNoticeList
에 설정하고, 사용자가 아직 확인하지 않은 (status가 '0'인) 알림 데이터의 개수를 setNoticeCount
에 설정한다.
useEffect(() => {
getNotice();
}, []);
→ useEffect
훅을 사용하여 컴포넌트가 마운트될 때 getNotice
함수를 실행한다.
// Notice.jsx
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { noticeCountState, noticeListState } from '../../recoil/Atom.js';
.
.
.
const Notice = () => {
const noticeList = useRecoilValue(noticeListState); // 알림 리스트 상태
const setPastNoticeList = useSetRecoilState(noticeListState); // 지난 알림 리스트
const setNoticeCount = useSetRecoilState(noticeCountState); // 알림 카운트 상태
.
.
.
const noticeClickHandler = async (groupId, participantId) => {
const participantIdString = JSON.stringify([participantId.toString()]);
try {
await UpdateNotificationStatus(participantIdString);
setPastNoticeList((prev) => {
return prev.map((notice) => {
if (notice['Participants.participantid'] === participantId) {
return { ...notice, 'Participants.status': 1 };
} else {
return notice;
}
});
});
setNoticeCount((prev) => prev - 1);
navigate(`/postmain/${groupId}`);
} catch (error) {
console.error(error);
}
};
const noticeList = useRecoilValue(noticeListState); // 알림 리스트 상태
const setPastNoticeList = useSetRecoilState(noticeListState); // 지난 알림 리스트
const setNoticeCount = useSetRecoilState(noticeCountState); // 알림 카운트 상태
→ useRecoilValue
와 useSetRecoilState
훅을 사용하여 알림 리스트와 알림 카운트 상태를 가져온다.
사용자가 알림을 클릭했을 때 noticeClickHandler
가 호출되고, 아래와 같이 실행된다.
await UpdateNotificationStatus(participantIdString);
→ 알림 상태(사용자가 읽었는지, 안 읽었는지)를 서버에 업데이트한다.
setPastNoticeList((prev) => {
return prev.map((notice) => {
if (notice['Participants.participantid'] === participantId) {
return { ...notice, 'Participants.status': 1 };
} else {
return notice;
}
});
});
→ 사용자가 알림을 클릭하여 읽었을 경우 해당 알림의 상태를 업데이트하여 지난 알림 리스트로 이동시킨다. 사용자가 클릭한 알림의 participantid
와 일치하는 알림의 status를 '1'로 변경하여 읽은 상태로 설정한다.
setNoticeCount((prev) => prev - 1);
→ 알림을 클릭한 경우 알림 개수를 1 감소시킨다.
navigate(`/postmain/${groupId}`);
→ 사용자가 읽은 알림의 해당 상세 페이지로 이동시킨다.
새로운 알림이 있을 경우 알림 아이콘 옆에 뱃지가 표시되어 사용자에게 새로운 알림 유무를 알린다.
// footer.js
import { useRecoilValue } from 'recoil';
import { noticeCountState } from '../../recoil/Atom.js';
function Footer() {
const noticeCount = useRecoilValue(noticeCountState);
return (
.
.
.
<NoticeContainer>
<StyledNavLink to='/notice'>
<IconComponents iconType='inbox' stroke='#4C4C4C' width="23" height="23" viewBox="0 0 23 23" />
{noticeCount > 0 && <CountBadge>{noticeCount}</CountBadge>}
</StyledNavLink>
</NoticeContainer>
const noticeCount = useRecoilValue(noticeCountState);
→ useRecoilValue
훅을 사용하여 noticeCountState
로부터 카운트의 상태 값을 가져온다.
{noticeCount > 0 && <CountBadge>{noticeCount}</CountBadge>}
→ noticeCount
가 0 이상인 경우에만 noticeCount
를 가져와 화면에 표시한다.