T
TanStack3y ago
conscious-sapphire

useQuery for search

Hi everyone! Was wondering if anyone could help me figure out the best way to implement a search page such as this with React Query. I would like a controlled search bar that the top which the user can type into. Although I do not want it to search until they have clicked the search button, I do want to be showing results initial so I would like it to fire a query when the page mounts with the default value. Below is the code I have managed to come up with. I am using derived state so that I can control the input and but not update the query key (this firing a new query) until the user clicks the search button. Is there a better solution than this which removes this bad practice? Thanks all! import { useState } from 'react' import { useQuery } from '@tanstack/react-query' import axios from 'axios' function App() { const [searchTerm, setSearchTerm] = useState("1") const [queryParams, setQueryParams] = useState(searchTerm) const { data } = usePerson(queryParams) const handleSearch = () => setQueryParams(searchTerm) return ( <div> <input onChange={(e) => setSearchTerm(e.target.value)} /> <button onClick={handleSearch}>Search</button> <div>{data?.name}</div> </div> ) } const usePerson = (queryParams: string) => { return useQuery({ queryKey: ["person", queryParams], queryFn: () => fetchPerson(queryParams) }) } const fetchPerson = async (queryParams: string) => { const { data } = await axios.get(https://swapi.dev/api/people/${queryParams}`) return data } export default App `
11 Replies
stormy-gold
stormy-gold3y ago
Use a enabled useState which you pass to useQuery and toggle it on click
conscious-sapphire
conscious-sapphireOP3y ago
But if I do that it means everytime the user types into the search bar it will fire a new query right?
stormy-gold
stormy-gold3y ago
No. Enabled is false initially.
conscious-sapphire
conscious-sapphireOP3y ago
If its initially false it will mean that I don't fetch data on mount which is what I am hoping to do. Then once it becomes enabled, it will mean that every time the user types into the searchbar the query key will change and a new query will be fired. I only want to fire a query when they click search I think this is what you are suggesting import { useState } from 'react' import { useQuery } from '@tanstack/react-query' import axios from 'axios' function App() { const [searchTerm, setSearchTerm] = useState("1") const [enabled, setEnabled] = useState(false) const { data } = usePerson(searchTerm, enabled) return ( <div> <input onChange={(e) => setSearchTerm(e.target.value)} /> <button onClick={() => setEnabled(true)}>Search</button> <div>{data?.name}</div> </div> ) } const usePerson = (searchTerm: string, enabled: boolean) => { return useQuery({ queryKey: ["person", searchTerm], queryFn: () => fetchPerson(searchTerm), enabled, }) } const fetchPerson = async (searchTerm: string) => { const { data } = await axios.get(https://swapi.dev/api/people/${searchTerm}`) return data } export default App `
stormy-gold
stormy-gold3y ago
Looks good. If you want to query initially: enabled: enabled || searchTerm === „1“
conscious-sapphire
conscious-sapphireOP3y ago
Its not quite what I'm looking for
stormy-gold
stormy-gold3y ago
Why not?
conscious-sapphire
conscious-sapphireOP3y ago
For these reasons
tame-yellow
tame-yellow3y ago
Couldn't you just only update searchTerm on submit? That way you only need one state. And it can be populated with default data.
conscious-sapphire
conscious-sapphireOP3y ago
Yeah I've gone with this approach, was trying to use a controlled input component from a UI lib originally which is why I was wondering about the best way to make it work with a controlled input
like-gold
like-gold3y ago
Your solution looks right. You definetly need intermidiate state if you want to only dowload query on click.
const [searchTerm, setSearchTerm] = useState("")

const [queryParams, setQueryParams] = useState("DEFAULT_PARAMS")

const { data } = usePerson(queryParams)

const handleSearch = () => setQueryParams(searchTerm)
const [searchTerm, setSearchTerm] = useState("")

const [queryParams, setQueryParams] = useState("DEFAULT_PARAMS")

const { data } = usePerson(queryParams)

const handleSearch = () => setQueryParams(searchTerm)
The only thing I would change is instead putting default_params in the searchTerm I would move it to queryParams this way when the page mounts the query will run for the first time with defaults, but your input will be clear.

Did you find this page helpful?