T
TanStack2y ago
extended-yellow

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
extended-salmon2y ago
Use a enabled useState which you pass to useQuery and toggle it on click
extended-yellow
extended-yellowOP2y 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
extended-salmon2y ago
No. Enabled is false initially.
extended-yellow
extended-yellowOP2y 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
extended-salmon2y ago
Looks good. If you want to query initially: enabled: enabled || searchTerm === „1“
extended-yellow
extended-yellowOP2y ago
Its not quite what I'm looking for
extended-salmon
extended-salmon2y ago
Why not?
extended-yellow
extended-yellowOP2y ago
For these reasons
flat-fuchsia
flat-fuchsia2y 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-yellow
extended-yellowOP2y 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
correct-apricot2y 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?