В общем виде, я бы делал как-то так:
const LIMIT = 10;
const getProducts = async (offset, limit) => { ... }
function App () {
const [offset, setOffset] = useState(0);
const [products, setProducts] = useState([])
useEffect(() => {
getProducts(offset, LIMIT).then(products => setProducts(v => [...v, ...products]))
}, [offset]);
return <RenderProducts onScroll={() => { // increment offset }} />
}
Конечно, ваш бекенд должен уметь отдавать данные по limit & offset.
И лучше вынести всю работу с данными в стор, тогда не нужен будет уродливый useEffect.
Что-то вроде (nanostores):
// store.ts
const LIMIT = 30;
export const $products = map({
offset: 0,
products: [],
isLoading: true
})
onMount($products, () => {
fetchProducts()
})
const fetchProducts = action($products, "fetchProducts", async store => {
const { products, offset } = store.get();
// TODO: add error handling
store.setKey("isLoading", true);
const newPartOfProducts = await api.getProducts(offset, LIMIT);
store.setKey("products", [...products, ...newPartOfProducts ]);
store.setKey("isLoading", false);
});
const incrementOffset = action($products, "incrementOffset", store => {
const { offset } = store.get();
store.setKey("offset", offset + LIMIT);
fetchProducts()
})
export const $productsMutations = { incrementOffset }
// App.tsx
function App () {
const { isLoading, products } = useStore($products)
return (
<Fragment>
<RenderProducts onScroll={$productsMutations.incrementOffset} products={products} />
{isLoading && <LoadingIndicator />}
</Fragment>
)
}