Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

first Item of data is wrong on useSWRInfinite #934

Closed
springkjw opened this issue Jan 28, 2021 · 5 comments
Closed

first Item of data is wrong on useSWRInfinite #934

springkjw opened this issue Jan 28, 2021 · 5 comments
Labels
area: pagination Pagination related issues help wanted Extra attention is needed

Comments

@springkjw
Copy link

springkjw commented Jan 28, 2021

Bug report

Description / Observed Behavior

I am implementing infinite scroll pagination using useSWRInfinite. However, after the third page scroll, the first item of data is not first page data but of the second page data.

Expected Behavior

When do page-nation, the first item in the data must be on the first page.

Repro Steps / Code Example

import {CategoryItem, Empty} from 'components/UI/atoms';
import {LoadingContext, SortContext} from 'context';
import _ from 'lodash';
import React, {useContext, useEffect, useMemo} from 'react';
import {useSelector} from 'react-redux';
import {useSWRInfinite} from 'swr';
import {axios, DEFAULT_PRICE_RANGE, MAX_PRICE, MIN_PRICE} from 'utils';

import * as style from './style';

export default function ItemList({
  prices = DEFAULT_PRICE_RANGE,
  brands = [],
  categories = [],
  shopId = null,
  isFetching = false,
  isFavorite = true,
}) {
  const gender = useSelector((state) => state.tendency.gender);
  const favoriteBrands = useSelector((state) => {
    return state.tendency.selectedBrands.map((fr) => fr.id);
  });

  const {sort} = useContext(SortContext);
  const {setLoading} = useContext(LoadingContext);

  const options = useMemo(() => {
    const data = {gender: gender ? 'W' : 'M', sort};

    if (prices[0] !== MIN_PRICE || prices[1] !== MAX_PRICE) {
      const minPrice = prices[0];
      const maxPrice = prices[1];

      if (minPrice >= maxPrice) {
        Object.assign(data, {
          minimumPrice: maxPrice,
          maximumPrice: minPrice,
        });
      } else {
        Object.assign(data, {
          minimumPrice: prices[0],
          maximumPrice: prices[1],
        });
      }
    }

    if (isFetching) {
      Object.assign(data, {
        fetchingPay: true,
      });
    }

    if (isFavorite && favoriteBrands.length > 0) {
      Object.assign(data, {
        brandIdList: favoriteBrands,
      });
    } else if (brands && brands.length > 0) {
      Object.assign(data, {
        brandIdList: brands,
      });
    }

    if (categories && categories.length > 0) {
      const newCategoires = categories.map((c) => c.id).filter((c) => !!c);

      if (newCategoires.length > 0) {
        Object.assign(data, {
          categoryIdList: newCategoires,
        });
      }
    }

    if (shopId) {
      Object.assign(data, {shopId});
    }

    return data;
  }, [
    categories,
    gender,
    sort,
    prices,
    isFetching,
    isFavorite,
    favoriteBrands,
    brands,
    shopId,
  ]);

  function getKey(pageIndex, previousPageData) {
    const opts = options;

    if (pageIndex > 0 && previousPageData) {
      const {id, modOfUpdatedAt} = previousPageData[
        previousPageData.length - 1
      ];
      Object.assign(opts, {bookmark: {itemId: id, modOfUpdatedAt}});
    }

    return ['/items/search', JSON.stringify(opts)];
  }

  async function fetcher(url, opts) {
    const data = JSON.parse(opts);
    const {data: resData} = await axios.post(url, data);
    const {results} = resData;
    return results;
  }

  const {data, error, size, setSize} = useSWRInfinite(getKey, fetcher, {
    compare(a, b) {
      if (a && b) {
        return _.isEqual(
          _.flatten(a, true)
            .filter((i) => !!i)
            .map((i) => i.id),
          _.flatten(b, true)
            .filter((i) => !!i)
            .map((i) => i.id),
        );
      }

      return false;
    },
  });

  const isLoadingInitialData = !data && !error;
  const isLoadingMore =
    isLoadingInitialData ||
    (size > 0 && data && typeof data[size - 1] === 'undefined');
  const isEmpty = data?.[0]?.length === 0;
  const isReachingEnd = isEmpty || (data && data[data.length - 1]?.length < 50);
  const items = data ? [].concat(...data) : [];

  useEffect(() => console.log(items.length > 0 ? items[0].id : null), [items]);

  useEffect(() => {
    setLoading(isLoadingMore);

    return () => axios.cancel();
  }, [isLoadingMore, setLoading]);

  function onLoadMore() {
    if (!isLoadingMore && !isReachingEnd) {
      setSize(size + 1);
    }
  }

  const memorizedItem = useMemo(
    () => ({item, index}) => (
      <CategoryItem data={item} position={index} refer="shopDetail" />
    ),
    [],
  );

  return (
    <style.Wrapper>
      <style.List
        data={items}
        numColumns={3}
        keyExtractor={(_, index) => `item-${index}`}
        columnWrapperStyle={style.Column}
        onEndReached={onLoadMore}
        onEndReachedThreshold={0.2}
        renderItem={memorizedItem}
        removeClippedSubviews
        scrollIndicatorInsets={{right: 1}}
        ListEmptyComponent={!isEmpty ? <Empty /> : null}
      />
    </style.Wrapper>
  );
}

Additional Context

SWR version. 0.4.0

@shuding shuding added help wanted Extra attention is needed area: pagination Pagination related issues labels Jan 28, 2021
@koba04
Copy link
Collaborator

koba04 commented Feb 2, 2021

@springkjw Could you reproduce this on a CodeSandbox Project or make the code simpler? This seems to be hard to confirm the issue.

@kamami
Copy link

kamami commented Feb 9, 2021

Same issue. First time loading data is undefined... Using swr 0.4.2

@koba04
Copy link
Collaborator

koba04 commented Feb 12, 2021

@kamami Could you reproduce it on a CodeSandbox Project? That would be very helpful.

@shuding
Copy link
Member

shuding commented Apr 3, 2021

@kamami do you mean that data is undefined in the first render? That is the expected loading state.

@shuding
Copy link
Member

shuding commented Jun 17, 2021

Will re-open this if we get new reports with detailed reproduction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: pagination Pagination related issues help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

4 participants