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
extended-salmon•2y ago
Use a enabled useState which you pass to useQuery and toggle it on click
extended-yellowOP•2y ago
But if I do that it means everytime the user types into the search bar it will fire a new query right?
extended-salmon•2y ago
No. Enabled is false initially.
extended-yellowOP•2y 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
`extended-salmon•2y ago
Looks good. If you want to query initially: enabled: enabled || searchTerm === „1“
extended-yellowOP•2y ago
Its not quite what I'm looking for
extended-salmon•2y ago
Why not?
extended-yellowOP•2y ago
For these reasons
flat-fuchsia•2y 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.
extended-yellowOP•2y 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
correct-apricot•2y ago
Your solution looks right. You definetly need intermidiate state if you want to only dowload query on click.
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.