const [items, setItems] = useState([]);
const [order, setOrder] = useState("createdAt");
const [offset, setOffset] = useState(0);
const [hasNext, setHasNext] = useState(false);
...
const handleDelete = (id) => {
const nextItems = items.filter((item) => item.id !== id);
setItems(nextItems);
};
const handleLoad = async (options) => {
const { reviews, paging } = await getReviews(options);
if (options.offset === 0) {
setItems(reviews);
} else {
setItems([...items, ...reviews]);
}
setOffset(options.offset + reviews.length);
setHasNext(paging.hasNext);
};
useEffect(() => {
handleLoad({ order, offset: 0, limit: LIMIT });
}, [order]);
const handleMore = () => {
handleLoad({ order, offset, limit: LIMIT });
};
...
<div>
<button onClick={handleMore}>더보기</button>
</div>
더보기 버튼을 누르고 데이터하나 삭제 버튼 눌렀을 때 (쓰로틀링 테스트)
위에서 더보기 버튼을 누르면
handleMore
함수가 실행되고, order, offset, limit라는 값으로handleLoad
함수를 실행함.
이 시점에서 items state값은 삭제버튼을 누르기전 state값임.
handleLoad
함수에서도 items state값은 삭제버튼을 누르기 전 state값임.getReviews
라는 비동기 함수를 실행하게 되는데, 네트워크를 보내기 전 다른 작업을 실행하게 됨. 이때 삭제버튼을 누른다면,handleDelete
함수가 호출되고 여기서 items state를 변경해서 요소를 하나 없앰.
그럼 요소가 지워진 채로 렌더링 됨.
그리고 나서 비동기 함수로 돌아오면,getReviews
함수에 response가 도착해서 이제 state를 변경하려고 하는데, 이때 items state값은
삭제되기 전에 items state값임.
그래서 지워진게 살아나서 렌더링되는 것처럼 보임.
비동기로 state를 처리할 때 잘못된 시점의 값을 처리하는 문제가 있음.
이럴 때 setter 함수의 값이 아니라 콜백을 전달하여 사용하면 됨.
파라미터로 이전 state값을 전달 받아, 변경할 state값을 return하면 됨.
사실상 현재 시점의 state값을 전달. (공식문서에는대기중인 state
라고 명시되어있네...)
[이전 state 값 사용하기 - react 공식 문서]
https://ko.react.dev/reference/react/useState#updating-state-based-on-the-previous-state
따라서 아래와 같이 setter함수에서 콜백을 사용해서 업데이트 함수 전달
const handleLoad = async (options) => {
const { reviews, paging } = await getReviews(options);
if (options.offset === 0) {
setItems(reviews);
} else {
setItems((prevItems) => [...prevItems, ...reviews]);
}
setOffset(options.offset + reviews.length);
setHasNext(paging.hasNext);
};