user8006446
user8006446

Reputation: 475

Filtering a fetched list from an API using React Query

I am using React Query (for the first time) to manage calls to an API. I am fetching a list of invoices, which I've done successfully so far in the code below. I now want to filter this list based on a status property that each invoice object has - it is either 'paid', 'pending' or 'draft - using a series of checkboxes.

How would I go about this? Could the filtering be somehow integrated into the initial call to the API so I am always receiving one set of data, or would I have to make multiple calls and render the data accordingly below?

import React from 'react'
import {useQuery} from 'react-query'
import InvoiceLink from './InvoiceLink'

const fetchData = async () => {
    const res = await fetch('http://localhost:3004/invoices');
    return res.json();
};


export default function InvoiceList() {
    const {data, isLoading, isError} = useQuery('invoices', fetchData);
    return (
        <div>
            <ul>
                <li><input type="checkbox" id="paid"></input><label htmlFor="paid">Paid</label></li>
                <li><input type="checkbox" id="pending"></input><label htmlFor="pending">Pending</label></li>
                <li><input type="checkbox" id="draft"></input><label htmlFor="draft">Draft</label></li>
            </ul> 
            {isError && (
                <p>Error</p>
            )}
            {isLoading && (
                <p>Loading</p>
            )}
            <ul>
            {data && data.map(item => 
                <InvoiceLink key={item.id} invoice={item} />
            )}
            </ul>
        </div>
    )
}

Upvotes: 14

Views: 26278

Answers (2)

TkDodo
TkDodo

Reputation: 28753

you can either do local filtering as described here, or, if you want to filter on the backend, you'd need to:

  • hold the filter criteria in local state
  • add the filter criteria to the query-key to trigger an automatic refetch (react-query refetches every time the key changes)
  • pass the filter criteria to the fetchFn and then to the backend
  • potentially use keepPreviousData: true for a better ux - see lagged queries

something like:

const fetchData = async (filter) => {
    const res = await fetch(`http://localhost:3004/invoices/${filter}`);
    return res.json();
};

const [filter, setFilter] = React.useState('all')
const {data, isLoading, isError} = useQuery(['invoices', filter], () => fetchData(filter));

additionally, I'd like to note that since you are using fetch, you need to transform erroneous responses into failed promises, because react-query expects a failed promise, but fetch doesn't do that out of the box. Please see here

Upvotes: 23

Yu Miao
Yu Miao

Reputation: 572

Only request the api once, filter based on the responsed data without react-query.

export default function InvoiceList() {
    const {data, isLoading, isError} = useQuery('invoices', fetchData);
    
    // filter the data here
    const isPaid = data.find(...)    
    const isPending = data.find(...)  
    const isDraft = data.find(...)  

    return (
        <div>
            <ul>
                <li><input type="checkbox" checked={isPaid} id="paid"></input><label htmlFor="paid">Paid</label></li>
                <li><input type="checkbox" checked={isPending} id="pending"></input><label htmlFor="pending">Pending</label></li>
                <li><input type="checkbox" checked={isDraft} id="draft"></input><label htmlFor="draft">Draft</label></li>
            </ul> 
            {isError && (
                <p>Error</p>
            )}
            {isLoading && (
                <p>Loading</p>
            )}
            <ul>
            {data && data.map(item => 
                <InvoiceLink key={item.id} invoice={item} />
            )}
            </ul>
        </div>
    )
}

Or filter data in fetchData method

const fetchData = async () => {
    const res = await fetch('http://localhost:3004/invoices');
    const data = res.json();
    return {
      data: data,
      isPaid: data.find(...),    
      isPending: data.find(...),  
      isDraft: data.find(...),  
    }
};

Upvotes: 5

Related Questions