igorkamyshev / farfetched

The advanced data fetching tool for web applications
https://ff.effector.dev
MIT License
192 stars 34 forks source link

Add createPagination and createInfinityScroll fabrics #332

Open Bricks666 opened 1 year ago

Bricks666 commented 1 year ago

Problems and use-cases

Problems Pagination and infinite scroll are common queries used a lot and mostly need a typical units for storing page, next parameters and etc. So This API will solve these problems make work with queries such types easier.

'Cause this PR is big, I split it on two parts. This one is about pagination. The second will be after this.

Use cases Common use-case it's flow where the page is identified by a number and the next(previous) also can be requested with page number, not link received from server. Pagination by server sent link may be classified as edge-case, 'cause it's not very popular, but sometimes used.

Analysis, pros, cons and pitfalls

Analysis I researched several libs/packages provided queries and looked at their pagination receipts.

The first group contains SWR, @tanstack/query and RTK-Query. All them don't provide special methods for pagination. It makes queries more flexible, but more times makes us write the same code. If try to find a reason for such decisions, I can guess that SWR and @tanstack/query were designed so for integration with view frameworks and managing from them.

The second group now contains only effector-pagination. This lib implement pagination fabric using effector units. This fabric manage current page, possibility to make request and caching. I think main pros it's encapsulation of change page logic and predicates of possibility fetch next and prev page.

Suggested implementation, compatibility with others APIs

I suggest to make pagination as extended type of Query. This will allow us to integrate pagination with keepFresh, retry and cache without much problems, 'cause it will be query with some extra fields and methods.

Interface may look like:

interface Pagination<Params extends RequiredPageParams, Data, Error, InitialData = null> extends Query<Params, Data, Error, InitialData> {
    $page: Store<number>;
    $hasNext: Store<boolean>;
    $hasPrev: Store<boolean>;
    next: Event<void>;
    prev: Event<void>;
    specific: Event<RequiredPageParams>;
}

And using like that:

import { createPagination } from "@farfetched/core";

const pagination = createPagination(...)

pagination.start({ ..., page: 1 }) // Request 1th page
pagination.next() // Request 2th page
pagination.prev() // Request 1th page again 
pagination.specific({ page: 43 }) // Request 43th page

Restrictions

  1. Params of passed handler/effect should extend RequiredPageParams interface which provides requested page and limit of requested items.

  2. How to determine the end of pagination? We may restrict shape of mapData response and make this function required. Function may return object like this:

    interface MapDataShape<Data> {
    data: Data;
    currentPage: number;
    hasNextPage: boolean;
    // Or change field on totalPages: number;
    hasPrevPage: boolean;
    }

    And then this object will be split filed to store.

  3. Base params(params passed into start method) will be used for next requests(next, prev, specific) with overriding page prop. But if user want to change one of it(even offset) he will need to call start method again. Base params will be got from $latestParams. Also user can't to fetch next and others pagination method if there're not $latestParams

  4. This implementation doesn't support pagination depended on server sent links at all. But cover the most popular cases with number pagination.

Pagination now

The first written in official example of farfetched and provide query with some stores for storing pages. I've not found any more.

igorkamyshev commented 1 year ago

Thanks for the PR. It's quite big, so I will take a look later.

Anyway, in Effector committee we are not starting with a code. Usually, we start with a description of a problem, with a design document and debates.

Could you show a design document to prepare me for reading your PR? 🙏

AlexandrHoroshih commented 1 year ago

Hello and thanks for the PR!

These features are supposed to cover a lot of complicated cases and design document is really needed 🙏

By design document i mean something like this:

  1. Describe the problem, that is being solved by this API. What are use-cases, what are edge-cases?
  2. Analysis of other existing solutions in other libraries (e.g. react-query). Are these built-in APIs or lower-level primitives for userland implementation? What are pros and cons? What are most common issues about these APIs? If possible to find out - why these apis where designed this way?
  3. Describe the suggested API. Why it is designed this way? Which known cases are covered and which are not? How it combines with other APIs, like retry or keepFresh? All these questions are most important
  4. Research and describe the alternatives. How userland implementation with already existing APIs looks like?

This is something we usually do, when new API is developed and this approach helps a lot. This way we get a lot better ideas and evade a lot of design mistakes or bugs before implementation even started

Bricks666 commented 1 year ago

Thanks for your reaction. You're right, I'll describe it.

igorkamyshev commented 1 year ago

@Bricks666 thanks for the description. I see that the description does not cover section “user land implementation”. Could you elaborate how users could solve this issue now without new features in the library? Typically, this section is a good source of inspiration for the invention of a new API.

usmanyunusov commented 1 year ago

Может использовать, как в attachOperation, уже созданный Query? image

Bricks666 commented 1 year ago

Sorry, I was very busy. This week or next one I'll look for it and write

@Bricks666 thanks for the description. I see that the description does not cover section “user land implementation”. Could you elaborate how users could solve this issue now without new features in the library? Typically, this section is a good source of inspiration for the invention of a new API.

Bricks666 commented 1 year ago

Может использовать, как в attachOperation, уже созданный Query? image

I'm not sure. What reason you might need base query in this situation?