I’m using React Query infinite queries to load a list of entries with a "next" button trigger (this is abbreviated code from the docs but it’s the same idea):
import { useInfiniteQuery } from '@tanstack/react-query'
function Projects() {
const {
data,
error,
fetchNextPage,
hasNextPage,
isFetching,
isFetchingNextPage,
status,
} = useInfiniteQuery({
queryKey: ['projects'],
queryFn: fetchProjects,
initialPageParam: 0,
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
})
return (
<>
<div>
{/* [loop through page data] */}
</div>
<div>
<button
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
>
Next Page
</button>
</div>
</>
)
}
When I do this Typescript automatically assumes the types of the Infinite Query variables like data
, error
, etc. using the types provided by the library. Now, I want to make the Next button its own component and pass the Infinite Query variables to it as a prop, like this:
// projects.tsx
import { useInfiniteQuery } from '@tanstack/react-query';
import NextButton from './nextbutton.tsx';
function Projects() {
const fetchProjects = async ({ pageParam }) => {
const res = await fetch('/api/projects?cursor=' + pageParam)
return res.json()
}
const {
data,
error,
fetchNextPage,
hasNextPage,
isFetching,
isFetchingNextPage,
status,
} = useInfiniteQuery({
queryKey: ['projects'],
queryFn: fetchProjects,
initialPageParam: 0,
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
})
const queryData = {
fetchNextPage: fetchNextPage,
hasNextPage: hasNextPage,
isFetchingNextPage: isFetchingNextPage
}
return (
<>
<div>
{/* [loop through page data] */}
</div>
<div>
<NextButton
queryData={ queryData }
/>
</div>
</>
)
}
// nextbutton.tsx
export default function NextButton({ queryData }) {
return (
<button
onClick={ () => queryData.fetchNextPage() }
disabled={ !queryData.hasNextPage || queryData.isFetchingNextPage }
>
Next Page
</button>
)
}
The trouble is that while the Infinite Query variables are typed automatically when they’re created, if I pass them as a prop to a separate component they have to be typed again.
I could just manually write out the type for each variable but it seems like there should be an easier, less repetitive way to type them. I’m fine with passing all of the variables to the button component instead of just a handful if that’s easier.
2
Answers
Do this. Your components should be self-contained and clearly define the props they expect. It makes your code portable, reusable and testable.
I would also use separate props instead of a single
queryData
object for clarity.You can directly tie your button props to the type from React Query…
or provide your own definition based on the expected usage…
Then set the component prop types…
and use this in your parent component
You don’t need to pass your whole query to the button component, this would be overkill
what you need to do is pass the function
fetchNextPage()
to the button and have it triggered with clicked on it, here is an example