3 - Add Autocomplete
In this step, we’ll implement Suggestions (Autocomplete) to offer real-time guidance as users type their queries. Suggestions help improve the user experience by showing relevant query options dynamically.
To retrieve suggestions, use the Raffle API’s /autocomplete
endpoint. This requires a uid
and a query
parameter, with an optional limit
parameter to control the number of suggestions returned.
Keep in mind that suggestions are generated after a certain number of questions have been asked. If the User Interface (UI) is new, there may be no suggestions available initially. We recommend implementing the logic once and letting the API handle the rest. A good approach is to display suggestions if they are available; otherwise, default to showing top questions.
Why Use a Debounced Value?
Fetching suggestions directly on every keystroke can overwhelm the API and negatively impact performance. To address this, we use a debounced value, which triggers the API call only after the user has paused typing for a short time. This ensures efficient use of resources.
We’ll use the useDebounce
hook from the @uidotdev/usehooks package to simplify the implementation.
API Call
Here’s the function to fetch suggestions from the Raffle API:
export const fetchSuggestions = async (query, limit) => {
const response = await fetch(
`https://api.raffle.ai/v2/autocomplete?uid=$YOUR_RAFFLE_UID&query=${query}&limit=${limit}`
);
const data = await response.json();
return data.suggestions;
};
This function sends a GET
request to the autocomplete
endpoint using the user’s query and returns a list of suggestions.
Integration with React
We’ll integrate the suggestions feature into the UI using React Query’s useMutation
and the useDebounce
hook for clean and efficient state management.
import { useEffect, useState } from "react";
import { useMutation } from "@tanstack/react-query";
import { useDebounce } from "@uidotdev/usehooks";
import { fetchSuggestions } from "./api";
export const Search = () => {
const [query, setQuery] = useState("");
const debouncedQuery = useDebounce(query, 500); // Debounce with 500ms delay
const { data: suggestions = [], mutate: handleFetchSuggestions } =
useMutation({
mutationKey: ["suggestions", debouncedQuery],
mutationFn: fetchSuggestions,
});
// Trigger suggestions when debouncedQuery is at least 3 characters long
useEffect(() => {
if (debouncedQuery.length >= 3) {
handleFetchSuggestions(debouncedQuery);
}
}, [debouncedQuery, handleFetchSuggestions]);
const handleSearch = () => {
if (query.trim()) {
console.log("Search triggered with query:", query.trim());
}
};
return (
<div>
<h2>Suggestions</h2>
<ul>
{suggestions.map(({ suggestion }, index) => (
<li
key={index}
onClick={() => {
setQuery(suggestion);
handleSearch();
}}
>
{suggestion}
</li>
))}
</ul>
</div>
);
};
Explanation
fetchSuggestions
: Fetches real-time suggestions from the/autocomplete
endpoint.useDebounce
: Prevents API calls on every keystroke by debouncing the input value.- Minimum Query Length: Ensures that suggestions are fetched only when the query is at least 3 characters long.
- Rendering Suggestions: Displays suggestions in a list and allows users to click on them to populate the search field.
By implementing debouncing and a character limit, this approach optimizes the API usage and ensures a smooth user experience.
Total Code
At this stage, your full code should look like this:
import { useEffect, useState } from "react";
import { useMutation, useQuery } from "@tanstack/react-query";
import { useDebounce } from "@uidotdev/usehooks";
import { fetchSuggestions, fetchTopQuestions } from "./api";
export const Search = () => {
const [query, setQuery] = useState("");
const debouncedQuery = useDebounce(query, 500); // Debounce with 500ms delay
const { data: topQuestions = [], isLoading } = useQuery({
queryKey: ["topQuestions"],
queryFn: fetchTopQuestions,
});
const { data: suggestions = [], mutate: handleFetchSuggestions } =
useMutation({
mutationKey: ["suggestions", debouncedQuery],
mutationFn: fetchSuggestions,
});
// Trigger suggestions when debouncedQuery is at least 3 characters long
useEffect(() => {
if (debouncedQuery.length >= 3) {
handleFetchSuggestions(debouncedQuery);
}
}, [debouncedQuery, handleFetchSuggestions]);
const handleSearch = () => {
if (query.trim()) {
console.log("Search triggered with query:", query.trim());
}
};
return (
<div>
<h1>Search API - React Example</h1>
<div>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Enter your query..."
/>
<button onClick={handleSearch}>Search</button>
</div>
<div>
<h2>Top Questions</h2>
{isLoading ? (
<p>Loading top questions...</p>
) : (
<ul>
{topQuestions.map(({ question }) => (
<li key={question} onClick={() => setQuery(question)}>
{question}
</li>
))}
</ul>
)}
</div>
<div>
<h2>Suggestions</h2>
<ul>
{suggestions.map(({ suggestion }, index) => (
<li
key={index}
onClick={() => {
setQuery(suggestion);
handleSearch();
}}
>
{suggestion}
</li>
))}
</ul>
</div>
<div>
<h2>Summary</h2>
<p>Summary will appear here...</p>
</div>
<div>
<h2>Search Results</h2>
<p>Search results will appear here...</p>
</div>
</div>
);
};