내가 지금 진행하고 있는 프로젝트에서
window.innerWidth가 가변적으로 변하는 즉, resize가 발생할때
component의 수가 width에 맞춰서 가변적
으로 변해야하는 경우에 대해
어떻게 처리 했는가? 를 기록하기 위해 이 글을 작성했다.
ref를 사용하여 dom 객체를 꺼내와서 getBoundingClientRect
를 사용하여 width값을 가지고 올까 했는데, 애초에 item이 10개라면 10개 모두 렌더링되어 ref를 사용하여 가져오는 dom에는 10개의 item이 렌더링되어 width값이 길어져 길어진 값을 가져올 것이라 판단하였다.
즉, getBoundingClientRect
를 사용하여 얻은 width 크기는 모든 item이 렌더링된 크기 일거라 생각하였다.
이전에, 노션페이지는 width크기가 가변적으로 변경되면 멈출때 딱 한번 resize가 되어서 알아본적이 있는데, window.onresize를 이용했다고 한다.
따라서, 노션은 window.onresize
를 이용하여 리플로우를 방지하였는데, 나도 이것을 한번 사용해보고 싶다고 생각만 했었는데, 드디어 사용해보게 되엇다!!
// getItemsOfList 는 전체 list에서 갯수를 입력해주면 그 만큼의 아이템을 가져오는 함수이다.
...
const [items,setItems] = useState(getItemsOfList(totalItems,window.innerWidth / SIZE));
window.onresize = () => {
setItems(getItemsOfList(totalItems,window.innerWidth / SIZE))
}
...
잘동작한다! 근데 어딘가 이상하다.
초기 렌더링이 나타나질 않는다..!
useEffect(() => {
setItems(getItemsOfList(totalItems,window.innerWidth / SIZE))
},[totalItems])
를 추가해주니 바로 잘 동작한다 ㅎㅎ..
엥? 근데 콘솔창을 보니 ??가 72번 찍혔다.. (onresize발생할때 console.log를 찍었습니다.)
그렇다! Debounce를 구현하여 Resize를 최소화할 예정이다!
debounce 코드는 아래와 같다.
export const debounce = (func: Function, ms: number) => {
let timeout: ReturnType<typeof setTimeout>;
return () => {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
func();
}, ms);
};
};
다음은 debounce를 적용한 resize 코드이다!
window.onresize = debounce(() => {
setItems(getItemsOfList(totalItems,window.innerWidth / SIZE))
},500)
잘 동작한다!
약 16ms (주사율) 마다 resize event가 발생하여 브라우저가 살려달라고 말하는것 같다. ( 사실, 이정도까지는 무리 없는 걸로 안다. )
width값의 변경이 멈춰서야 비로소 렌더링이 진행된다!
정확한 기준으로 비교를 하지는 못했지만,
위 이미지를 통해 resize를 멈춰야 reflow가 진행되는 것과 매 16ms 마다 reflow가 발생하는건 차이가 있다는 것을 알 수 있다.
window.onresize는 useEffect로 감싸주세요! cleanUp도 필수입니다!