๐ ๋ณธ ๊ธ์ 2022๋ 7์ 23์ผ ๊ฐ์ธ github์ ์์ฑ๋ ๊ธ์ ๋๋ค.
๐ Scroll ์ด๋ฒคํธ ๋์ Intersection Observer API ์ด์ฉํ์!
3์ฐจ ํ๋ก์ ํธ ๋ฆฌํฉํ ๋ง!
Scroll ์ด๋ฒคํธ๋ฅผ ํ์ฉํ ๋ฌดํ ์คํฌ๋กค!
๐ฑ์ ์ฒด ์ผ๊ธฐ ๋ณด์ฌ์ฃผ๋ ํ์ด์ง๋ฅผ ์ปค์ ๊ธฐ๋ฐ ๋ฌดํ ์คํฌ๋กค๋ก ๊ตฌํํ์๋๋ฐ, ์ด๋ window ๊ฐ์ฒด์ scroll ์ด๋ฒคํธ๋ฅผ ์ฐ๊ฒฐํ์ฌ ํน์ ์ง์ ์ ์คํฌ๋กค์ด ๋์์ ๋, ์๋ฒ์ ๋ค์ ์ผ๊ธฐ ๋ฆฌ์คํธ๋ฅผ ์์ฒญํ๊ณ ์ด๋ ํ๋ผ๋ฏธํฐ์ cursor ๊ฐ์ ๋ณด๋ด๋ ํ์์ผ๋ก ํ์๋ค!
// handleScroll.js
export const handleScroll = () => {
const scrollHeight = document.documentElement.scrollHeight;
const scrollTop = document.documentElement.scrollTop;
const clientHeight = document.documentElement.clientHeight;
if (scrollTop + clientHeight >= scrollHeight) {
return true; // true ๊ฐ ๋ฐํ
}
};
// EmotionList.js
...
const [isLoaded, setIsLoaded] = useState(true);
const [stop, setStop] = useState(false);
useEffect(() => {
if (isLoaded && !stop) {
getList();
}
}, [isLoaded]);
useEffect(() => {
window.addEventListener(
'scroll',
function (event) {
const res = handleScroll(event);
if (res === true) { // true ๊ฐ์ด ๋ฐํ๋ ๋
setIsLoaded(true); // setIsLoaded(true)๋ก ์ํ ๋ณ๊ฒฝ
}
},
false
);
}, []);
const getList = async () => {
if (isLoaded === true) {
try {
const res = await Api.get(`diary/list/?cursor=${cursor}`);
const length = res.data.length;
const sliceData = res.data.slice(0, length - 1);
setCursor(res.data.slice(-1)[0].cursor);
setDiaryList((data) => [...data, ...sliceData]);
setIsLoaded(false);
if (length < 10) {
setStop(true);
}
} catch (err) {
snackBar('info', '๋ ์ด์ ์์ฑํ ์ผ๊ธฐ๊ฐ ์์ต๋๋ค. ');
}
}
};
๋ฌธ์
๐ฅ- ์์ฒญํ ์ผ๊ธฐ ๋ฆฌ์คํธ๋ ๋ ์ด์ ์์ง๋ง, ์คํฌ๋กค๋ก ์ธํด ๋ถํ์ํ ์์ฒญ ๋ฐ์.
- ์คํฌ๋กค ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด ์ด๋ฒคํธ๊ฐ ํธ์ถ๋์ด ๋ฉ์ธ ์ค๋ ๋ ์ฑ๋ฅ์ ์ข์ง ์์.
๋ฌธ์ -1 ํด๊ฒฐ
๐กstop ์ด๋ผ๋ ์ํ๊ฐ ์ค์
์ฌ์ฉ์๊ฐ ๋ฐ์์จ ์ผ๊ธฐ ๋ฆฌ์คํธ๊ฐ 10๊ฐ ๋ฏธ๋ง์ด๋ผ๋ฉด(์ผ๊ธฐ ๋ฆฌ์คํธ 10๊ฐ์ฉ ๋ฐ์์ด.)
๋ ์ด์ ์ผ๊ธฐ ๋ฆฌ์คํธ๊ฐ ์๋ค๋ ๋ป์ด๊ธฐ ๋๋ฌธ์ stop ์ํ๊ฐ์ true๋ก ๋ณ๊ฒฝ!
์คํฌ๋กค ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ฌ๋, stop ๊ฐ์ด true ์ด๊ธฐ๋๋ฌธ์ getList ํจ์ ํธ์ถ X.
const [stop, setStop] = useState(false);
useEffect(() => {
if (isLoaded && !stop) { // stop ๊ฐ์ด false ์ผ๋๋ง ํธ์ถ.
getList();
}
}, [isLoaded]);
useEffect(() => {
window.addEventListener(
'scroll',
function (event) {
const res = handleScroll(event);
if (res === true) { // true ๊ฐ์ด ๋ฐํ๋ ๋
setIsLoaded(true); // setIsLoaded(true)๋ก ์ํ ๋ณ๊ฒฝ
}
},
false
);
}, []);
const getList = async () => {
if (isLoaded === true) {
try {
const res = await Api.get(`diary/list/?cursor=${cursor}`);
const length = res.data.length;
const sliceData = res.data.slice(0, length - 1);
setCursor(res.data.slice(-1)[0].cursor);
setDiaryList((data) => [...data, ...sliceData]);
setIsLoaded(false);
if (length < 10) {
setStop(true);
}
// ๋ง์ฝ ์๋ฒ์์ ์ ๋ฌ๋ฐ์ ์ผ๊ธฐ๋ฆฌ์คํธ๊ฐ 10๊ฐ ๋ฏธ๋ง์ด๋ผ๋ฉด
// ์ผ๊ธฐ ๋ฆฌ์คํธ๊ฐ ๋ ์ด์์๋ค๋ ๋ป -> stop ์ํ๊ฐ true ๋ก ๋ณ๊ฒฝ.
} catch (err) {
snackBar('info', '๋ ์ด์ ์์ฑํ ์ผ๊ธฐ๊ฐ ์์ต๋๋ค. ');
}
}
};
๋ฌธ์ 1์ด ์๋ฒฝํ๊ฒ ํด๊ฒฐํ์ง ๋ชปํ๋ค. ์๋ฅผ ๋ค์ด ์ฌ์ฉ์์ ์ผ๊ธฐ๊ฐ 30๊ฐ๋ผ๋ฉด? 10 -> 10 -> 10
๊ธธ์ด๋ 10๊ฐ ์ด๊ธฐ๋๋ฌธ์ stop ์ํ๊ฐ์ด ๋ณ๊ฒฝ๋์ง ์๋๋ค.
๋ฌธ์ -2 ํด๊ฒฐ ๋ฐฉ๋ฒ์ ?
๐ค๊ณ ๋ฏผ ๋์ Throttle ์ ์ฌ์ฉํ์ฌ ์ต์ ํ ์์ ์ ํ๋ ค๊ณ ํ๋ค.
Throttle ?
๐์ด๋ฒคํธ์ ์ํ ์ฝ๋ฐฑ์ ์ผ์ ์๊ฐ ๋ค์ ํธ์ถ.
์ด๋ฐ ์ฑ์ง์ ์ด์ฉํด์, scroll ์ด๋ฒคํธ์ ์ ์ฉํ์ ๋ ์ค์ ํ ์๊ฐ๋์์ ์คํฌ๋กค์ ํ๋๋ผ๋ ์ฝ๋ฐฑ์ด ํธ์ถ๋์ง ์๋๋ค.
Intersection Observer API โญ๏ธ
โญ๏ธThrottel ์ ๋ํด ์์๋ณด๋ ์ค Intersection Observer API ์ ์๊ฒ ๋์๋ค!
โ๏ธ ๊ต์ฐจ ๊ด์ฐฐ์ API(Intersection Observer API)๋ ์์์์ ๋๋ ์ต์์ ๋ฌธ์์ ๋ทฐํฌํธ ์
๋์ ์์์ ๊ต์ฐจ์ ์์ ๋ณํ๋ฅผ ๋น๋๊ธฐ์ ์ผ๋ก ๊ด์ฐฐํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ค.
์ฝ๊ฒ ๋งํด, scroll ์ด๋ฒคํธ์ ๋ฌ๋ฆฌ ํ๊ฒ ์์๊ฐ ๋ค๋ฅธ ์์์ ๋ค์ด๊ฐ๊ฑฐ๋ ๋๊ฐ ๋ or
์ง์ ํ ๋งํผ ๋ ์์์ ๊ต์ฐจ ๋ถ๋ถ์ด ๋ณ๊ฒฝ๋ ๋๋ง๋ค ๋ฑ๋กํ ์ฝ๋ฐฑํจ์๋ฅผ ์คํํ ์ ์๋ค!
=> ๋ฌธ์ 2 ํด๊ฒฐ!
์์ ํ ์ฝ๋
โ๏ธ useEffect(() => {
let observer;
if (target) {
observer = new IntersectionObserver(getList, {
threshold: 0.7,
});
observer.observe(target);
}
return () => observer && observer.disconnect();
}, [target]);
const getList = async ([entry]) => {
if (entry.isIntersecting && !stop) {
try {
setLoading(true);
const res = await Api.get(`diary/list/?cursor=${cursor}`);
if (res.data.length === 0) {
setStop(true);
setLoading(false);
} else {
const length = res.data.length;
const sliceData = res.data.slice(0, length - 1);
setCursor(res.data.slice(-1)[0].cursor);
setDiaryList((data) => [...data, ...sliceData]);
setLoading(false);
}
} catch (err) {
snackBar('info', '๋ ์ด์ ์์ฑํ ์ผ๊ธฐ๊ฐ ์์ต๋๋ค. ');
}
}
};
๐ Reference
https://developer.mozilla.org/ko/docs/Web/API/Intersection_Observer_API
'๐ WIL > ๐ React' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
TDD ๋์ ํด๋ณด์.(2) - TypeScript ์ ์ ์ฉ (0) | 2022.08.27 |
---|---|
TDD ๋์ ํด๋ณด์.(1) - React Testing Library ? Jest ? (0) | 2022.08.24 |
React Query (์ด์ TanStack Query ๋ผ๊ณ ๋ถ๋ฆฌ๋ ..) ์ ์ฉ (1) (0) | 2022.08.19 |
๐งฌ Atomic ๋์์ธ์ ์ ์ฉํด๋ณด์. (0) | 2022.08.13 |
โ๏ธ useRef (0) | 2022.08.10 |