Trouble Shooting
[map / useState]파베에서 데이터 받아왔는데 배열에 업데이트가 안된다
봄나물소년
2023. 11. 30. 01:55
파베에서 데이터 받아와서 클라이언트 측에 저장할라했더니 안됩니다
const [posts, setPosts] = useState<any>([]);
useEffect(() => {
async function getData() {
const db = getFirestore(firebasedb);
const querySnapshot = await getDocs(collection(db, "posts"));
querySnapshot.docs.map((doc) => {
setPosts([...posts, doc.data()])
});
}
getData();
}, []);
파이어스토어에서 불러온 데이터 배열을 map을 돌려서 posts 배열에 업데이트 시키고 싶었다
querySnapshot.docs.map((doc) => {
console.log(doc.data()) // 데이터가 하나하나 잘 들어온다
setPosts([...posts, doc.data()]) // map 한 번 돌때 마다 이전 데이터를 그대로 받아오고 새로운 데이터를 넣어준다
});
이랬더니 useEffect 밖에서 찍어준 posts의 결과가 이상했다.
각기 다른 데이터가 2개가 있다 치면, 첫 번째 데이터가 2개 들어와있는 배열이 있거나.
데이터가 하나만 들어온 배열이 찍히는 것이다.
결과가 매번 다른 것도 이상하다.
// 실패코드 예상 실행 로직
// 데이터는 하나씩 잘 찍히는데, posts를 찍어보면 첫번째 데이터 하나만 들어와있음
// 추측1) 업뎃 순서가 보장되지 않아서???
// map의 실행 시퀀스
// 1번쨰 실행: setPosts에 의해 [{원소1}]로 업데이트
// 2번쨰 실행: setPosts에 의해 이전의 값 [{원소1}]가져와 [{원소1}, {원소2}]로 업데이트
// ...
// n번째 실행: (위와 같음)
// 배열의 length만큼 로직의 실행이 끝났으면 종료.
map안에서 돌때마다 setPosts로 업데이트 해주기
vs
map리턴값 받아서 변수에 할당 후 해당 변수를 setPosts에 업데이트
의 차이인데 왜 실패코드는 하나의 데이터만 담은 posts를 주는걸까??????
바로 함수형 setState와 map을 제대로 알지 못했기 때문이었다.
setPosts에 새로운 객체를 전달했을 땐 비동기작업이 여러번 일어나면 예상치 못한 동작이 일어날 수 있다. 개판됨.
그러니 함수형 setPosts를 함수형으로 작성하면 이전 상태값 받아서 새로운 상태 반환하니까 비동기 작업이 이전 상태 기준으로 이뤄지기에 원하는대로 업데이트를 보장해준다.
map은 배열을 반환한다. 그니까 이걸 변수에 담아서 setPosts에 새로운 객체로 담아주면 되는거였다.
useEffect(() => {
async function getData() {
const db = getFirestore(firebasedb);
const querySnapshot = await getDocs(collection(db, "posts"));
// data에 map의 리턴값을 담는다
const data = querySnapshot.docs.map((doc) => ({
...doc.data(), id: doc.id
}));
// data 객체를 setPosts의 인자로 담는다
setPosts(data);
}
getData();
}, []);
해결.
추가 질문))))
이것도 되는디?
useEffect(() => {
async function getData() {
const db = getFirestore(firebasedb);
const querySnapshot = await getDocs(collection(db, "posts"));
querySnapshot.docs.map((doc) => {
setPosts((prevValue: any) => [...prevValue, doc.data()])
});
}
getData();
}, []);
map의 리턴값을 변수에 담지않고 그냥 안에서 함수형 setState를 써서 작성한 코드도 되긴한다!
하지만 map의 사용목적에 맞지 않다.
이런 경우엔 forEach를 쓰는 것이 메소드의 제대로된 사용법이다.
바보같은 자문이었지만 메소드 개발하신 분도 이거보면 뿌듯해할듯.
끝