- vừa được xem lúc

Tạo một Infinite Scroll Component với React Hook

0 0 26

Người đăng: Pham Tuan Viet

Theo Viblo Asia

Chắc hẳn những bạn đã từng sử dụng Facebook, Instagram hay Reddit trước đây đều biết đến tính năng cuộn (kéo) nội dung vô cực.

Tiếp tục series React, chúng ta sẽ đi sâu hơn vào việc phát triển một ứng dụng React của bạn với việc làm như thế nào để xây dựng một ứng dụng Infinite Scroll sử dụng React Hook. (Nếu bạn chưa hiểu về React Hook hãy tìm hiểu tại đây). Thay vì người dùng liên tục phải nhấp vào Next Page thì nội dung mới sẽ tự động được tải liên tục khi người dùng đến cuối trang.

Có rất nhiều thư viện tích hợp sẵn ứng dụng này nhưng nếu bạn muốn thử thách bản thân cùng tôi tìm hiểu và thực hiện xây dựng nó từ đầu thì hãy theo dõi bài này nhé.

Build InfiniteList

Đầu tiên, tạo nhanh một Component đơn giản. Chúng ta sẽ khởi tạo một state trống và tham chiếu đến statesetState như một InfiniteList Component như sau:

export default function App() { const [state, setState] = useState([]); return ( <div className='App'> <InfiniteList state={state} setState={setState} /> </div> );
};

InfiniteList component này sẽ trả về một danh sách dữ liệu được cung cấp sẵn từ API.

Khi request API thành công, chúng ta sẽ đẩy dữ liệu về các parent's state. Chúng ta phải đảm bảo được rằng dữ liệu sẽ không ghi đè lên các dữ liệu đã lấy ra trước đó bằng cách sử dụng một hàm getData() để nạp dữ liệu tránh trùng lặp.

Trong ứng dựng này chúng ta sẽ sử dụng tạm API fetch data từ DOG CEO: https://dog.ceo/api/breeds/image/random/<records_number> Example:

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> );

Chúng ta sẽ cho hiển thị 15 hình ảnh đầu tiên. Sau đó cần một sự kiện để luôn lắng nghe khi người dùng cuộn xuống cuối trang và thực hiện lấy thêm dữ liệu bằng các gọi lại hàm getData().

Xác định vị và bắt sự kiện Scroll

Chúng ta cùng phân tích việc bắt sự kiện này nhé. Tôi hy vọng phần dưới các bạn sẽ giúp bạn hiểu.

  1. window.innerHeight — cố định (static), chiều cao của cửa sổ trình duyệt
  2. window.scrollY — di chuyển (dynamic), vị trí hiện tại của thanh scroll.
  3. list.clientHeight — cố định (static), chiều cao của phần từ <ul> <ul/>.
  4. list.offsetTop — cố định (static) khoảng cách từ đầu trang tới phần tử <ul>.

Nếu (1) + (2) === (3) + (4), chúng hiểu rằng người dùng đã cuộn đến cuối trang và cần nạp thêm dữ liệu.

Trong trường hợp phần tử ul được fix cứng chiều cao. Chúng ta cần lấy các thuộc tính của phần từ này.

  1. element.scrollHeight — tổng chiều cao có thể cuộn trang.
  2. element.scrollTop — vị trí hiện tại của thanh scroll.
  3. element.clientHeight — chiều cao hiện tại của phần từ (đã bao gồm overflow).

Vậy thì khi (1) === (2) + (3) người dùng sẽ chạm đến cuối trang.

Chúng ta sẽ bắt những trường hợp này lại với nhau bằng việc sử dụng userEffect trong Hook. Để liên tục lắng nghe sự kiện cuộn từ trình duyệt chúng ta sẽ sự kiện addEventListener. Nhưng để tránh các lỗi tiềm ẩn, chúng ta cần remove đối tượng này khỏi useEffect khi việc thực thi component này kết thúc như sau:

useEffect(() => { widnow.addEventListener('event', function); return () => { window.removeEventListener('event', function); }
}, [])

Xây dựng ứng dụng đơn giản

Trước đó, chúng ta đã cùng nhau phân tích cách xác định vị trí và bắt sự kiện scroll như thế nào. Dưới đây sẽ là đoạn code nhỏ mô tả lại toàn bộ ứng dụng như sau:

App.js
import React, { useState } from 'react';
import InfiniteList from './InfiniteList'; export default function App() { const [state, setState] = useState([]); return ( <div className='App'> <InfiniteList state={state} setState={setState}/> </div> );
};

InfiniteList.js
import React, { useState, useEffect } from 'react'; export default function InfiniteList(props) { const [loadMore, setLoadMore] = useState(true); useEffect(() => { getData(loadMore); setLoadMore(false); }, [loadMore]); useEffect(() => { const list = document.getElementById('list') if(props.scrollable) { // list has fixed height list.addEventListener('scroll', (e) => { const el = e.target; if(el.scrollTop + el.clientHeight === el.scrollHeight) { setLoadMore(true); } }); } else { // list has auto height  window.addEventListener('scroll', () => { if (window.scrollY + window.innerHeight === list.clientHeight + list.offsetTop) { setLoadMore(true); } }); } }, []); useEffect(() => { const list = document.getElementById('list'); if(list.clientHeight <= window.innerHeight && list.clientHeight) { setLoadMore(true); } }, [props.state]); const getData = (load) => { if (load) { fetch('https://dog.ceo/api/breeds/image/random/15') .then(res => { return !res.ok ? res.json().then(e => Promise.reject(e)) : res.json(); }) .then(res => { props.setState([...props.state, ...res.message]); }); } }; return ( <ul id='list'> { props.state.map((img, i) => <li style={{backgroundImage: `url(${img})`}} key={i}/>) } </ul> );
};

Chúng ta có thể demo tại đây. DEMO

Như vậy, một ứng dụng cơ bản về Infinite Scroll sử dụng React Hooks đã được xây dựng. Tuy nhiên còn hơi cơ bản và sơ sài, hy vọng sau bài viết này sẽ có nhiều đóng góp để có thể hoàn thiện hơn cho các bài viết về sau.

Cảm ơn các bạn đã đọc bài viết này.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Giới thiệu Typescript - Sự khác nhau giữa Typescript và Javascript

Typescript là gì. TypeScript là một ngôn ngữ giúp cung cấp quy mô lớn hơn so với JavaScript.

0 0 523

- vừa được xem lúc

Cài đặt WSL / WSL2 trên Windows 10 để code như trên Ubuntu

Sau vài ba năm mình chuyển qua code trên Ubuntu thì thật không thể phủ nhận rằng mình đã yêu em nó. Cá nhân mình sử dụng Ubuntu để code web thì thật là tuyệt vời.

0 0 395

- vừa được xem lúc

Đặt tên commit message sao cho "tình nghĩa anh em chắc chắn bền lâu"????

. Lời mở đầu. .

1 1 730

- vừa được xem lúc

Tìm hiểu về Resource Controller trong Laravel

Giới thiệu. Trong laravel, việc sử dụng các route post, get, group để gọi đến 1 action của Controller đã là quá quen đối với các bạn sử dụng framework này.

0 0 357

- vừa được xem lúc

Phân quyền đơn giản với package Laravel permission

Như các bạn đã biết, phân quyền trong một ứng dụng là một phần không thể thiếu trong việc phát triển phần mềm, dù đó là ứng dụng web hay là mobile. Vậy nên, hôm nay mình sẽ giới thiệu một package có thể giúp các bạn phân quyền nhanh và đơn giản trong một website được viết bằng PHP với framework là L

0 0 448

- vừa được xem lúc

Bạn đã biết các tips này khi làm việc với chuỗi trong JavaScript chưa ?

Hi xin chào các bạn, tiếp tục chuỗi chủ đề về cái thằng JavaScript này, hôm nay mình sẽ giới thiệu cho các bạn một số thủ thuật hay ho khi làm việc với chuỗi trong JavaScript có thể bạn đã hoặc chưa từng dùng. Cụ thể như nào thì hãy cùng mình tìm hiểu trong bài viết này nhé (go).

0 0 433