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.
/autocomplete
uid
query
limit
Keep in mind that suggestions are generated after a certain number of questions have been asked. If the Tool (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.
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.
useDebounce
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.
GET
autocomplete
We’ll integrate the suggestions feature into the UI using React Query’s useMutation and the useDebounce hook for clean and efficient state management.
useMutation
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> ); };
fetchSuggestions
By implementing debouncing and a character limit, this approach optimizes the API usage and ensures a smooth user experience.
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> ); };
4 - Add Summary