'How to implement pagination and search in React

Good evening! I am creating simple React app using TS + React Query + Recoil. App is about 'online library'. I would like to create pagination and search input (to find specific author or title). My idea was, when app starts I am fetching data from page 1. Then when I'll click 2nd button on my pagination bar I'll fetch data from page 2 etc. Code looks like this:

Main component

import { useGetBooks } from '../../hooks/useGetBooks';
import { BookType } from '../../types/Book';
import { SingleBook } from './SingleBook';
import styled from 'styled-components';
import { Navbar } from './Navbar';
import { Loader } from '../utilities/Loader';
import { Error } from '../utilities/Error';
import { useState } from 'react';
import { useRecoilState } from 'recoil';
import { Books } from '../../recoil/globalState';

type bookType = BookType;

export const BookList = () => {
    const [pageNumber, setPageNumber] = useState(1);
    const [books, setBooks] = useRecoilState(Books);
    const { isLoading, isError } = useGetBooks(pageNumber, setBooks);

    if (isLoading) {
        return <Loader isLoading={isLoading} />
    }

    if (isError) {
        return <Error />
    }

    const displayBooks = books.data.map((book: bookType) => {
        return (
            <SingleBook key={book.id} book={book} />
        )
    })

    return (
        <BookContainer>
            <div className='test'>
                <button onClick={() => setPageNumber((page) => page - 1)} disabled={pageNumber == 1}>Prev page</button>
                <p>{books.metadata.page}</p>
                <button onClick={() => setPageNumber((page) => page + 1)} disabled={books.metadata.records_per_page * books.metadata.page > books.metadata.total_records}>Next page</button>
            </div>
            <Navbar />
            <BookContent>
                {displayBooks}
            </BookContent>
        </BookContainer>
    )
}

React query:

import { useQuery } from "react-query";
import axios from 'axios';

const fetchBooks = async (pageNumber: number) => {
    const res = await axios.get(`http://localhost:3001/api/book?page=${pageNumber}`);
    return res.data
}

export const useGetBooks = (pageNumber: number, setBooks: any) => {
    return useQuery(['books', pageNumber], () => fetchBooks(pageNumber),
        {
            onSuccess: (data) => setBooks(data),
            keepPreviousData: true
        })
}

Recoil:

import { atom } from 'recoil';

export const Books = atom({
    key: 'book',
    default: [] as any
})

And books response:

books: {
  data: [
    {
    author: 'Some crazy',
    title: 'Some crazy'
    },
        {
    author: 'Some crazy1',
    title: 'Some crazy1'
    },
  ],
  metadata: {
    page: 1,
    records_per_page: 10,
    total_records: 17
  }
 
}

Search Input Implementation:

import React, { useState } from "react"
import { useGetFilteredBooks } from "../../hooks/useGetFilteredBooks"
import { useRecoilState } from "recoil"
import { Books } from "../../recoil/globalState"



export const SearchBook = () => {
    const [text, setText] = useState('')
    const [books, setBooks] = useRecoilState(Books);
    const { data, refetch } = useGetFilteredBooks(text, setBooks);

    const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        setText(event.target.value)
    }

    const handleOnSubmit = (e: any) => {
        e.preventDefault();
        refetch();
    }


    return (
        <>
            <form onSubmit={handleOnSubmit}>
                <Input value={text} onChange={handleOnChange} placeholder="Enter the name of the book or author" />
                <button type="submit">Show</button>
            </form>
            <button onClick={() => console.log(books)}>plasda</button>
        </>
    )
}

React query:

import { useQuery } from "react-query";
import axios from 'axios';

const fetchFilteredsBooks = async (searchText: string) => {
    const res = await axios.get(`http://localhost:3001/api/book?search=${searchText}`);
    return res.data
}

export const useGetFilteredBooks = (searchText: string, setBooks: any) => {
    return useQuery(['filteredBooks', searchText], () => fetchFilteredsBooks(searchText),
        {
            onSuccess: (data) => setBooks(data),
            enabled: false
        })
}

We can only display 10 items per 1 page.

PROBLEM:

When we search something and we get data back, we can have scenario, when data will need to be display not on 1 page. So when we have filtered data, and we click 2nd button on pagination, the filtered data will disapeared and we see not filtered data from page 2



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source