Reactで無限スクロールを実装する。(Handson,react-query,react-infinite-scroller)

## はじめに
[前回](https://qiita.com/koji0705/items/42c8b387da007c458eac)のライブラリ利用版です。react-queryとreact-infinite-scrollerを組み合わせた実装です。

https://twitter.com/kkfactory0705/status/1718159737286709594?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1718159737286709594%7Ctwgr%5E6fc33e2bdd0c8b90be42f993ed07481d47fd0cc9%7Ctwcon%5Es1_c10&ref_url=https%3A%2F%2Fqiita.com%2Fembed-contents%2Ftweetqiita-embed-content__a5f1285da8f80d91b1618c95583c2782

## 成果物
https://github.com/kouji0705/react_infinite_scroll

```shell:ディレクトリ構成
~/develop/HITOTSU/react_infinity_scroll$ tree -I node_modules 
.
├── README.md
├── package.json
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── logo192.png
│   ├── logo512.png
│   ├── manifest.json
│   └── robots.txt
├── src
│   ├── App.tsx
│   ├── components
│   │   ├── ReactQueryInfiteScrollSample.tsx
│   │   └── UseInfiniteScrollSample.tsx
│   ├── hooks
│   │   └── useInfiniteScroll.tsx
│   ├── index.tsx
│   ├── logo.svg
│   └── type.ts
├── tsconfig.json
└── yarn.lock

5 directories, 17 files
```

```tsx:index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import {QueryClient, QueryClientProvider} from 'react-query';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);
const queryClient = new QueryClient();

root.render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </React.StrictMode>
);
```

```tsx:App.tsx
import React from 'react';
import { UseInfiniteScrollSample } from './components/UseInfiniteScrollSample'

function App() {
  return (
    <UseInfiniteScrollSample />
  );
}

export default App;
```

```tsx:src/components/ReactQueryInfiteScrollSample.tsx
import {FC, useRef} from 'react';
import InfiniteScroll from 'react-infinite-scroller';
import React from 'react';
import axios from 'axios';
import {useInfiniteQuery} from 'react-query';

export const ReactQueryInfiteScrollSample: FC = () => {
  const containerRef = useRef(null);
  const LIMIT = 10;

  const fetchData = async (page: number) => {
    console.log('fetchData call page', page);
    const response = await axios.get<Post[]>(`https://jsonplaceholder.typicode.com/posts?_page=${page}`);
    const data = response.data;
    return data;
  };

  const {data, isLoading, isFetching, hasNextPage, fetchNextPage} = useInfiniteQuery(
    'todos',
    ({pageParam = 1}) => fetchData(pageParam),
    {
      getNextPageParam: (lastPage, allPages) => {
        const nextPage = lastPage.length === LIMIT ? allPages.length + 1 : undefined;
        return nextPage;
      },
    }
  );

  if (isLoading) return <div />;

  return (
    <div>
      <div style={{height: '2000px'}}>無限スクロール</div>
      {isFetching && <div className="loading">Loading...</div>}
      <div ref={containerRef}>
        <InfiniteScroll loadMore={(page) => fetchNextPage({pageParam: page})} hasMore={hasNextPage}>
          {data?.pages.map((page, i) => (
            <div key={i}>
              {page.map((item: Post) => (
                <div key={item.id}>
                  <p>
                    {item.id}：{item.title}
                  </p>
                </div>
              ))}
            </div>
          ))}
        </InfiniteScroll>
      </div>
    </div>
  );
};

interface Post {
  userId: number;
  id: number;
  title: string;
  body: string;
}

```