firebase의 firestore를 이용해 개발을 진행하던 도중, useEffect 내에 실시간으로 추가, 수정, 삭제 되는 데이터를 전역 상태로 보관하는 코드를 작성했다.
처음에는 당연히 아래와 같이 useEffect 훅 내에서 컴포넌트에서 가지고 있는 가장 최신 state Snapshot을 가지고 올 수 있을거라고 생각했다.
const [state, setState] = useState([])
useEffect(() => {
setState([...state, ...ArrayData])
}, [ArrayData])
하지만 firebase onSnapshot 메서드를 통해 실시간 통신을 진행할 경우 ...state를 이용할 수 없었고, 아래처럼 setState의 prevState 인자를 이용해야 했다.
아래는 Map 객체로 관리하고 있는 recoil state를 업데이트 하는 코드이다.
const setKanbanDataState = useSetRecoilState(kanbanState);
useEffect(() => {
...
...
const kanbanQuery = query(
collection(db, "project", pathname, "data"),
where("is_deleted", "==", false),
);
const unsubKanban = onSnapshot(kanbanQuery, (kanbanSnapshot) => {
const addedMap = new Map();
kanbanSnapshot.docChanges().forEach((change) => {
// 최초 Snapshot 생성 혹은 사용자가 직접 칸반을 추가했을 때
if (change.type === "added") {
addedMap.set(change.doc.id, change.doc.data());
}
// 칸반을 수정할 경우
if (change.type === "modified") {
setKanbanDataState((prev) => {
prev.set(change.doc.id, change.doc.data());
return new Map([...prev]);
});
}
// 칸반이 삭제된 경우 (is_deleted 수정 시 쿼리 결과 변경)
if (change.type === "removed") {
setKanbanDataState((prev) => {
prev.delete(change.doc.id);
return new Map([...prev]);
});
}
});
if (addedMap.size > 0) {
setKanbanDataState((prev) => new Map([...prev, ...addedMap]));
}
});
...
...
}, [])
그럼 prevState는 뭘 하는 녀석일까?
react의 setState는 기본적으로 비동기적으로 실행된다. 성능상 이점을 가져가기 위해 리액트에서 여러개의 setState를 묶어서 실행시키기 때문인데,
이때 setState의 prevState를 활용하게 된다면 이전 state를 확정적으로 가져올 수 있다.
따라서 이전 state가 로직 내에서 반드시 필요하다면 prevState를 사용하는 것이 권장된다.