Can someone help me in implementing the debounce functionality using creatApi with query implementation from redux toolkit.
Thanks in advance.
Can someone help me in implementing the debounce functionality using creatApi with query implementation from redux toolkit.
Thanks in advance.
I personally didn't find any debounce implementation in RTK Query out-of-the-box. But you can implement it yourself.
Define an api. I'm using an openlibrary's one:
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
type BooksSearchResult = {
docs: Book[];
};
type Book = {
key: string;
title: string;
author_name: string;
first_publish_year: number;
};
export const booksApi = createApi({
reducerPath: 'booksApi',
baseQuery: fetchBaseQuery({ baseUrl: 'http://openlibrary.org/' }),
endpoints: builder => ({
searchBooks: builder.query<BooksSearchResult, string>({
query: term => `search.json?q=${encodeURIComponent(term)}`,
}),
}),
});
export const { useSearchBooksQuery } = booksApi;
Next thing you need is debounce hook, which guarantees that some value changes only after specified delay:
function useDebounce(value: string, delay: number): string {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
Use debounce hook on your search form:
import React, { useEffect, useState } from "react";
import BookSearchResults from "./BookSearchResults";
function useDebounce(value: string, delay: number): string {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
const DebounceExample: React.FC = () => {
const [searchTerm, setSearchTerm] = useState("");
const debouncedSearchTerm = useDebounce(searchTerm, 500);
return (
<React.Fragment>
<h1>Debounce example</h1>
<p>Start typing some book name. Search starts at length 5</p>
<input
className="search-input"
type="text"
placeholder="Search books"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<BookSearchResults searchTerm={debouncedSearchTerm}></BookSearchResults>
</React.Fragment>
);
};
export default DebounceExample;
Use the search query hook in search results component. It uses its own state for search term value, which is very convenient if you want to add extra "filters" for debounced value (for example, start query only when search term's length is greater than some value).
import React, { useState, useEffect } from "react";
import { useSearchBooksQuery } from "./booksApi";
type BookSearchResultsProps = {
searchTerm: string;
};
const BookSearchResults: React.FC<BookSearchResultsProps> = ({
searchTerm
}: BookSearchResultsProps) => {
const [filteredSearchTerm, setFilteredSearchTerm] = useState(searchTerm);
const { data, error, isLoading, isFetching } = useSearchBooksQuery(
filteredSearchTerm
);
const books = data?.docs ?? [];
useEffect(() => {
if (searchTerm.length === 0 || searchTerm.length > 4) {
setFilteredSearchTerm(searchTerm);
}
}, [searchTerm]);
if (error) {
return <div className="text-hint">Error while fetching books</div>;
}
if (isLoading) {
return <div className="text-hint">Loading books...</div>;
}
if (isFetching) {
return <div className="text-hint">Fetching books...</div>;
}
if (books.length === 0) {
return <div className="text-hint">No books found</div>;
}
return (
<ul>
{books.map(({ key, title, author_name, first_publish_year }) => (
<li key={key}>
{author_name}: {title}, {first_publish_year}
</li>
))}
</ul>
);
};
export default BookSearchResults;
Full example is available here.
after the first render our request hook will try to send the request we can bypass this with the skipToken the request will not be sent until searchTerm returns some value
import { useDebounce } from 'use-debounce'
import { skipToken } from '@reduxjs/toolkit/query'
const [storeName, setStoreName] = useState('')
const [searchTerm] = useDebounce(storeName, 1500)
const { data } = useSearchStoresRequestQuery(searchTerm || skipToken)
more about skipToken: https://redux-toolkit.js.org/rtk-query/usage/conditional-fetching
and also inside my useSearchStoresRequestQuery
endpoints: (builder) => ({
getStoresWithSearchRequest: builder.query({
query: ({searchTerm}) => {
return {
url: `admin/v1/stores?searchTerm?${searchTerm}`,
method: 'GET',
}
},
In my case the following solution worked well.
I used component way of debouncing. Use any debounce fn, in my way:
npm i debounce
And then in component
import debounce from 'debounce';
const Component = () => {
const { data, refetch } = useYourQuery();
const [mutate] = useYourMutation();
const handleDebouncedRequest = debouce(async () => {
try {
// you can use refetch or laze query way
await refetch();
await mutate();
} catch {}
}, 2000);
// ...other component logic
}
© 2022 - 2024 — McMap. All rights reserved.