Nieskończone przewijanie z React Hooks
Funkcja nieskończonej listy (przewijania) istnieje już od jakiegoś czasu. Jest to świetny sposób na poprawę jakości UX w Twojej aplikacji.
Dostępnych jest wiele bibliotek, które powstały właśnie do takich celów, ale jeśli tak jak ja, chcesz rzucić sobie wyzwanie, to zbadaj ze mną implementację od zera, używając w tym celu Hooks.
Zbudujmy zatem na szybko prosty komponent aplikacji. Inicjalizujemy pusty stan i przekazujemy referencje do state
i setState
jako propy do naszego komponentu InfiniteList
.
export default function App() {
const [state, setState] = useState([]);
return (
<div className='App'>
<InfiniteList
state={state}
setState={setState}
/>
</div>
);
};
Komponent InfiniteList
zwróci nieuporządkowaną listę zdjęć piesków pobranych z Dog API.
Kiedy nasze żądanie się powiedzie, przekazujemy zdjęcia do stanu rodzica. Upewnijmy się, że nie nadpiszemy poprzedniego stanu, ponieważ będziemy używać funkcji getData()
wielokrotnie, aby pobrać więcej obrazów. A zatem - kropka, kropka, kropka . . .
function InfiniteList(props) {
useEffect(() => {
getData();
}, []);
const getData = () => {
fetch('https://dog.ceo/api/breeds/image/random/15')
.then(res => {
return !res.ok
? res.json().then(e => Promise.reject(e))
: return res.json()
}
.then(res => props.setState([...props.state, ...res.message]))
.catch(console.log);
}
return (
<ul id='list'>
{ props.state.map((img, i) => (
<li key={i} style={{backgroundImage: `url(${img})`}}/>)}
</ul>
);
Jak dotąd, wyświetla nam to pierwsze 15 obrazów.
Teraz musimy nasłuchiwać zdarzenia, gdy użytkownik przejdzie do samego dołu listy i pobierze więcej obrazów, przywołując getData()
ponownie.
Ale jak określamy dolny zasięg? Mam nadzieję, że ten obrazek to wyjaśni.
window.innerHeight
- statyczne, wysokość okna przeglądarki użytkownika.window.scrollY
- właściwość dynamiczna, aktualna pozycja przewijania.list.clientHeight
- statyczne, wysokość kontenera (element ul).list.offsetTop
- wcięcie kontenera (jeśli występuje) od góry strony, statyczne.
Zatem, jeśli 1+2===3+4, wiemy, że użytkownik znajduje się na dole strony i musimy rozpocząć pobieranie.
Ale co, jeśli chcemy, żeby ul miało stałą wysokość? Musimy pobrać właściwości ul
:
element.scrollHeight
- wartość statyczna, całkowita przewijalna wysokość.element.scrollTop
- wartość dynamiczna, aktualna pozycja górnego przewijania.element.clientHeight
- wartość statyczna, aktualna wysokość elementu (bez overflow).
W związku z tym, 1===2+3 oznacza, że użytkownik dosięgnął dna kontenera. Teraz możemy warunkowo stwierdzić, czy jest to element o stałej wysokości, ustawiając właściwość scrollable
.
Zbierzmy to wszystko w nowy hook useEffect
.
Jest jeszcze jedna, ostatnia poprawka do wprowadzenia, żeby to wszystko zadziałało. Zainicjujemy stan, a następnie, gdy spełniony zostanie powyższy warunek, przełączymy stan, dając mu sygnał do pobierania, uzależniając nasz pierwszy hook od wartości stanu.
const [loadMore, setLoadMore] = useState(false);
useEffect(() => {
setLoadMore(false);
getData();
}, [loadMore]);
Możemy również warunkowo renderować spinner ładujący na dole, czekając na odpowiedź API, inicjując nowy stan z pewną wartością prawdziwą, a następnie resetując go, gdy żądanie zostanie spełnione.
Ponadto, aby uniknąć potencjalnych błędów, możemy usunąć listenera zdarzeń z obiektu okna, gdy komponent zostanie odmontowany, dodając instrukcję zwrotną w useEffect
w ten sposób:
useEffect(() => {
widnow.addEventListener('event', function);
return () => {
window.removeEventListener('event', function);
}
}, [])
Linki
Dzięki za przeczytanie i miłego kodowania!
Oryginał tekstu w języku angielskim przeczytasz tutaj.