huolalatech / react-query-kit Goto Github PK
View Code? Open in Web Editor NEW๐๏ธ A toolkit for ReactQuery that make ReactQuery hooks reusable and typesafe
License: MIT License
๐๏ธ A toolkit for ReactQuery that make ReactQuery hooks reusable and typesafe
License: MIT License
How can I access queryClient to invalidate queries in onSuccess callback?
If an error is throw inside the queryFn
, for example by fetch or some api client, it is not caught and returned in the error field.
This will crash the render or trigger an Error Boundary somewhere down the line.
This is different from the normal behavior of useQuery
and I believe this is a bug.
import { QueryClient, dehydrate } from '@tanstack/react-query'
import { createQuery } from 'react-query-kit'
type Response = { title: string; content: string }
type Variables = { id: number }
const usePost = createQuery<Response, Variables, Error>({
primaryKey: '/posts',
queryFn: ({ queryKey: [primaryKey, variables] }) => {
/* api library throws some error */
throw new Error("some network error")
// return fetch(`${primaryKey}/${variables.id}`).then(res => res.json())
},
enabled: (data) => !data,
suspense: true
})
const variables = { id: 1 }
export default function Page() {
/* error should be returned here and not thrown */
const { data, error } = usePost({ variables, suspense: true })
return (
{ error ? (
<div>Something went wrong.</div>
) : (
<div>
<div>{data?.title}</div>
<div>{data?.content}</div>
</div>
)}
)
}
I couldn't make global error type in react-query-kit
. I have an code block like below that works for @tanstack/react-query but not works with react-query.
import "@tanstack/react-query";
declare module "@tanstack/react-query" {
interface Register {
defaultError: IError;
}
}
when I call a query with this configuration, the error defined below is IError
type.
const { error} = useQuery({
...
})
How can I make the same effect for react-query-kit
I'm trying to update the react-query-kit version from 2.0.7
to 2.0.8
but I have the following error in the build
The requested module '@tanstack/react-query' does not provide an export named 'useSuspenseQuery'
. I'm also using "@tanstack/react-query": "4.36.1"
.
Is there a way to fix this issue? Should I update the version of tanstack/react-query?
I encountered an npm error when trying to use react-query-kit
with @tanstack/react-query
version 5.0.0-rc.1
. The current peerDependencies
configuration restricts compatibility to @tanstack/react-query
version 4.x, leading to dependency conflicts.
Error Message:
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: [email protected]
npm ERR! Found: @tanstack/[email protected]
npm ERR! node_modules/@tanstack/react-query
npm ERR! @tanstack/react-query@"^5.0.0-rc.1" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer @tanstack/react-query@">= 4.0.0" from [email protected]
npm ERR! node_modules/react-query-kit
npm ERR! react-query-kit@"^2.0.1" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force or --legacy-peer-deps
https://blog.greenkeeper.io/introduction-to-version-ranges-e0e8c6c85f0f
To resolve this issue and allow compatibility with @tanstack/react-query version 5.x and beyond, we should update the peerDependencies configuration in react-query-kit to the following:
"peerDependencies": {
"@tanstack/react-query": "^4 || ^5"
}
This change will permit users to use the latest version of @tanstack/react-query without encountering dependency conflicts.
It would be nice if the enabled
param could accept either a boolean or a function. like this:
import { QueryClient, dehydrate } from '@tanstack/react-query'
import { createQuery } from 'react-query-kit'
type Response = { title: string; content: string }
type Variables = { id: number }
const usePost = createQuery({
queryKey: ['posts'],
enabled: (variables: Variables) => !!variables.id,
fetcher: (variables: Variables): Promise<Response> => {
return fetch(`/posts/${variables.id}`).then(res => res.json())
},
})
I have defined a global custom error type and I can easily access error type without any type error.
import "@tanstack/react-query";
declare module "@tanstack/react-query" {
interface Register {
defaultError: IError;
}
}
But, when I define a custom error like above, I get the data below as TFnData.
export const useGetSettingsQuery = createQuery({
queryKey: ["some", "query", "key"],
fetcher: async (params) => {
const { data: response } = await backendAgent.get("....");
return response IGetSettingsResponse;
},
});
const { data, error } = useGetSettingsQuery({....});
// the type of error variable is IError here
// but the type of data variable is not IGetSettingsResponse. I will get it as TFnData generic
How can I fix this bug
Can we expose queryKeyHashFn so we can just access it like usePost.queryKeyHashFn
const usePost = createQuery<Response, Variables, Error>({
primaryKey: '/posts',
queryKeyHashFn: () => 'some-custom-hash-fn'
queryFn: ({ queryKey: [primaryKey, variables] }) => {
// primaryKey equals to '/posts'
return fetch(`${primaryKey}/${variables.id}`).then(res => res.json())
},
})
Hi,
I have two seperate middlewares named companMiddleware
and languageMiddleware
. Company middleware works successfully, it gets the data and injects into the variables. But, somehow language middleware does not update query key. I don't understand why it does happen.
const companyMiddleware: Middleware = (useQueryNext) => {
return (options) => {
const companyId = useCompanyId();
return useQueryNext({
...options,
enabled: !!companyId && options.enabled,
variables: { ...options.variables, companyId },
});
};
};
const languageMiddleware: Middleware = (useQueryNext) => {
return (options) => {
const { currentLang } = useLocales();
const fullKey = getKey(options.queryKey, options.variables);
return useQueryNext({
...options,
queryKey: [...fullKey, currentLang.value],
});
};
};
export const useGetAnalyticsQuery = createQuery({
queryKey: ["analytics", "get"],
fetcher: async (params: IGetAnalyticsRequest, context) => {
const { data: response } = await backendAgent.post(
"/some_url",
params,
);
return response.body.content as IGetAnalyticsResponse;
},
use: [companyMiddleware, languageMiddleware],
});
Could you help me out
Hey, I just found this library, and it has exciting ideas. Thank you! I have a few questions, though.
I tried to reimplement your optimistic update example but moved the implementation into the mutation definition instead of using the hook instance (a similar question was raised in this issue #21). This implementation is more common because I want to have optimistic updates working for every instance of the hook, no matter how many times I use it in the app and don't copy over the implementation for every instance.
This is what I got codesandbox
I found a few issues there:
useDefaultOptions
returns an options object, the onError/onSettle
context type is inferred incorrectly (infers unknown
instead of { previousTodos: Todos }
- the return type of the onMutate
callback.If I remove useDefaultOptions
and pass options to createMutation
directly, the context type is inferred correctly.
createMutation({ mutationFn, onMutate, onError }) // correct context type
createMutation({ mutationFn, useDefaultOptions: () => ({ onMutate, onError }) }) // unknown context type
useDefaultOptions
should receive the options object from the hook as an argument. Otherwise, if both options are used, the hook options override the ones from useDefaultOptions
and there's no way to access the options used in the hook instance. Make sure options don't override each other.Using the optimistic update code as an example, I want to clear the input after a user clicks the add button:
const useAddTodo = createMutation({ mutationFn, useDefaultOptions: () => ({ onMutate, onError }) })
...
const addTodoMutation = useAddTodo({
// When mutate is called:
onMutate: async () => {
setText("");
},
});
If I do this, the onMutate
from useAddTodo
replaces the onMutate
in createMutation
.
If useDefaultOptions
would receive options as an argument, it could be fixed like this:
const useAddTodo = createMutation({
mutationFn, useDefaultOptions: (options) => ({
onMutate: (newTodo) => {
// optimistic update logic goes here
// then you can run onMutate from useAddTodo()
options?.onMutate?.(newTodo)
}
})
})
setData
for setQueryData
with queryClient
and queryKey
built in. What do you think of adding other most used methods? Now your optimistic updates example looks inconsistent because it used a mixed API. It uses todosQuery.setData()
but at the same time await queryClient.cancelQueries(todosQuery.queryKey);
and queryClient.invalidateQueries(useTodos.getKey())
.It will be nicer if you expose other methods like getData for queryClient.getQueryData
, invalidateQuery
for queryClient.invalidateQueries
etc.
I.e.
const { methods: { getData, setData, invalidateQuery } } = useTodos()
Maybe make it configurable so devs can decide which methods to expose.
queryKey
as a hook return and getKey()
as a static method? getKey
seems self-sufficient for this purpose. queryKey
does the same, but it makes API look inconsistent.Hi! Let me start by saying thank you for making this amazing library. I am trying to upgrade from v1.4.8
to v2.0.1
and im having a hard time wrapping my mind around how to use middlewares to invalidate queries. I have a lot of examples such as the following:
export const useCreateProject = createMutation<void, { name: string }>({
mutationFn: variables => axios.post(getProjectEndpoint(), toPartial(toProjectDTO)(variables)),
useDefaultOptions: () => {
const queryClient = useQueryClient();
const projectsQuery = useProjects;
return {
onSuccess: () => {
queryClient.invalidateQueries(projectsQuery.getKey());
},
};
},
});
Since the useDefaultOptions
was deprecated how can i achieve the same functionality now? It could also be useful if I could pass the queryClient
to all onSuccess
(im not sure if this will be part of the context i.e. ctx
passed as the 3rd parameter)
Hi, I'm migrating to version 3, I'm following the instructions but I keep getting this error
[Bug] queryFn is not supported, please use fetcher instead.
on this code:
const { data: projectOverviews, isLoading } = createQuery(
getAllProjects.getOptions() as unknown as CreateQueryOptions<ProjectMDL[], void, Error>,
)();
within getAllProjects I'm using the new fetcher function. So, I don't understand why I still get this error, any idea what is causing this?
Thank you
I have a simple setup from doc examples. Note that the mutation function doesn't require any arguments.
const useAddTodo = createMutation({
mutationFn: async () =>
new Promise((resolve) => {
setTimeout(resolve, 1000)
}),
})
And this works perfectly fine.
const todo = useAddTodo()
...
todo.mutate()
But as soon as I add a middleware to this setup
const mutationMiddleware: Middleware<MutationHook> = (useMutationNext) => {
return (options) => {
return useMutationNext(options)
}
}
const useAddTodo = createMutation({
mutationFn: async () =>
new Promise((resolve) => {
setTimeout(resolve, 1000)
}),
use: [mutationMiddleware]
})
TS complains that mutate
expected 1-2 arguments but got 0.
In /src/index.ts file, you have exports of CreateQueryOptions
, CreateInfiniteQueryOptions
, CreateSuspenseQueryOptions
and CreateSuspenseInfiniteQueryOptions
types.
But CreateMutationOptions
export is missing.
CreateMutationOptions
to named exportsexport * from "./types";
Hi there,
The latest middleware update is an excellent addition to the library. Do you think it makes sense to remove setData
from the library API because the end user can implement it using middleware?
const any = useAny()
------^ infered as Response type
any is typeof Response | undefined
https://codesandbox.io/s/reproduce-react-query-kit-data-type-with-initialdata-is-undefined-8f886n
in plain react-query initialData works as expected
export const usePlainTodos = ()=>{
const result = useQuery<Todos>({
queryKey: ["/api/data"],
queryFn: ({ queryKey: [url] }) => axios.get(url).then((res) => res.data),
initialData: {
items: [],
ts: 1
},
})
const {data}= result;
/**
* ----^^^
* const data: Todos
*/
}
I want to inject return values from hooks into query/mutation.
In plain React Query, I cound achieve that by doing like so:
const useUserQuery = () => {
const { userId } = useAuth();
const queryClient = useQueryClient();
return useQuery({
queryKey: ["/user/"],
queryFn: async () => {
return (await http.get(`/user/${userId}`)).data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["todos"] });
},
});
};
How should I write similar code to implement this?
Do you think it's okay to do like so?
const useUserQuery = (arg: Parameters<typeof query>[0]) => {
const { userId } = useAuth();
const queryClient = useQueryClient();
const query = useMemo(
() =>
createQuery({
primaryKey: "/user/",
queryFn: async ({ queryKey: [primaryKey] }) => {
return (await http.get(`${primaryKey}${userId}`)).data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["todos"] });
},
}),
[queryClient, userId]
);
return query(arg);
};
In my use Mutation router, I found out the Error and Variable types are swapped.
The router
export const authRouter = router("auth", {
verifyEmail: router.mutation({
mutationFn: verifyEmail,
}),
login: router.mutation({
mutationFn: loginUser,
}),
register: router.mutation({
mutationFn: registerUser,
}),
});
The function
export async function loginUser(data: { email: string; password: string }) {
const res = await api.post<LoginUserResponse>("/login", data);
return res.data;
}
Hi! Thanks for the new version. The middleware is an excellent idea and provides many additional opportunities.
I'm experimenting with new middleware and trying different hypothetical scenarios. I want to enrich the hook and add a built-in fetchQuery
method (can be any of queryClient's methods) .
const { fetchQuery } = usePosts()
const handleClick = async () => {
await fetchQuery()
...
}
The way I see it is implemented via the middleware below. The one thing is missing - It would be great to have the exposed methods available in the middleware (i.e. getKey etc). If methods would be available as an argument, the code might look like this:
const fetchQueryAttacher = (useQueryNext) => {
return (options, methods) => { // expose methods here
const queryClient = useQueryClient()
const hook = useQueryNext(options)
return Object.assign({}, hook, {
fetchQuery: () => {
return queryClient.fetchQuery(methods.getFetchOptions())
},
})
}
}
What do you think? It's still unclear how to type this, but this is a different issue.
Trying to do something like:
import { createQuery } from 'react-query-kit';
export const useUsers = createQuery<Response, Variables, Error>(queryOptions);
results in a typescript error of
Exported variable 'useUsers' has or is using name 'QueryHook' from external module "path/to/project/node_modules/.pnpm/[email protected]_djz4nnd5briyzzf typescript (4023)
Exporting QueryHook from the library allows me to:
import type { QueryHook } from '../../../node_modules/react-query-kit/src/index';
import { createQuery } from 'react-query-kit';
export const useUsers: QueryHook<Response, Variables, Error> = createQuery<Response, Variables, Error>(queryOptions);
That's a solution but it would be nicer if typescript could infer the type for useUsers
.
We manage a heirarchy of things like below so invalidating becomes easier. React query kit is awesome but the primary key being a string prevents implementation of heirarichal key thing. Should we add or any other workaround ???
const todoKeys = {
all: ['todos'] as const,
lists: () => [...todoKeys.all, 'list'] as const,
list: (filters: string) => [...todoKeys.lists(), { filters }] as const,
details: () => [...todoKeys.all, 'detail'] as const,
detail: (id: number) => [...todoKeys.details(), id] as const,
}
Hello,
Readme file mentions createQueryClient
function which should allow to register middlewares globally. The search repo result says that there is no any such functionality defined. Do you have any plans to implement this feature?
Thank you for this great package.
Do you have any plan for createQueries()
to support useQueries()
?
https://tanstack.com/query/v4/docs/react/reference/useQueries
router was introduced in v3.0.0
.
In a way, it acts very similar to TRPC Routers.
However unlike tRPC
, react-query-kit
atm doesn't allow merging/nesting multiple routers.
And I think in its current state it's not super convenient to use.
The idea behind a router
is to logically group and map 1 backend API endpoint to 1 custom query hook.
And the backend's API is never 'flat'. Endpoints can have any number of sub-segments in the URL.
Imagine, if my backend has these endpoints available:
/api/users
/api/users
/api/users/command/report
/api/users/command/promote
Current README example proposes either to create 1 router for everything or use a plain js object to merge 2 routers into one.
However, the /users/command
router is clearly a sub-group of /users
. So I would like to nest the command
router inside of the users
router. And react-query-kit
doesn't provide a clear way to achieve this.
For me, the ideal router config for those endpoints would look something like this:
import { router } from "react-query-kit";
const command = router("command", {
report: router.mutation(...),
promote: router.mutation(...)
})
const api = router("users", {
command, // <-- nest router inside of another router
get: router.query(...),
create: router.mutation(...)
})
const api = router("api", {
users, // <-- nest router inside of another router
// Add other routers here in the future
// ...
})
Then, I can use the created api
router like this:
api.users.get.useQuery();
api.users.create.useMutation();
api.users.command.report.useMutation();
api.users.command.promote.useMutation();
Extend react-query-kit
router
's functionality to include nesting/merging child routers.
Update Typescript types to allow nesting routers.
Use this example as a useful reference
Hello, I would like to use onSuccess callback from both mutation definition and hook in component. So I have something like this:
Mutation definition:
export const useUploadImage = createMutation({
mutationFn: async (variables: ExpandedUploadImageRequest) => ImagesCollection.uploadImage(variables),
onSuccess: (data, variables) => {
//add new image data into infinite query data (img url, guid,...)
},
});
Component:
const Component: React.FC = () => {
const uploadImage = useUploadImage({
onSuccess: (data) => { //only this one is called
// show image preview in this component
},
});
//...
const handleUpload = useCallback(() => {
//....
uploadImage.mutate(uploadArgs);
}, [uploadImage, uploadArgs]);
}
Currently, when i call my uploadImage mutation and it succeeds, only onSuccess callback from the component is called. When I remove it, the one in mutation definition is called.
Is there some way to call both of them? Or from inside the onSuccess in component call the "parent" one? Thanks
The following code has correct type in on success argument
const useAccountCreateMutation = createMutation<SeedAccountDto, SeedAccountRequest, Error>({
mutationKey: ['account', 'create'],
mutationFn: async (variables) =>
ApiLayer.accounts.seedCreate(variables).then((res) => res.data.data!),
onSuccess: (response, variables) => {
console.log(variables);
},
});
export { useAccountCreateMutation };
Typescript is yelling at me when I'm passing select options, but it's actually working
import axios from "axios";
import { createInfiniteQuery } from "react-query-kit";
interface Projects {
data: { id: number; name: string }[];
previousId: number;
nextId: number;
}
export const useProjects = createInfiniteQuery<Projects>(
"/api/projects",
async ({ queryKey: [url], pageParam = 0 }) => {
const res = await axios.get(`${url}?cursor=${pageParam}`);
return res.data;
},
{
getPreviousPageParam: (firstPage) => firstPage.previousId ?? undefined,
getNextPageParam: (lastPage) => lastPage.nextId ?? undefined,
select: (page) => {
console.log("page", page);
return page;
}
}
);
Variables from useDefaultOptions are actually never passed to queryFn.
const usePost = createQuery<Response, Variables, Error>({
primaryKey: '/posts',
queryFn: ({ queryKey: [primaryKey, variables] }) => {
// variables is actually undefined here
// ...
},
useDefaultOptions: () => ({ variables: { id: 42 }})
})
Passing variables to hook instance works however.
In the recently updated examples, you explicitly receive queryClient
as a second argument and pass it through. What is it for? Why do you pass it to useQueryClient
explicitly?
const myMiddleware = useQueryNext => {
return (options, queryClient) => {
const client = useQueryClient(queryClient)
return useQueryNext(
options,
queryClient
)
}
}
Currently we only can statically provide mutationKey to the generated mutation.
This results in single unified mutation, but sometimes we need different mutation key for different variables.
There should be a way to provide keys dynamically.
Proposal:
I'm trying to pass data from my middleware to the mutationFn
. The only argument mutationFn
has is variables
but I don't see variables
exist in mutationMiddleware
options.
const mutationMiddleware: Middleware<MutationHook> = (useMutationNext) => {
return (options) => {
const variables = { foo: 'bar' }
return useMutationNext({ ...options, variables }) // TS2353: Object literal may only specify known properties, and variables does not exist in type
}
}
const useAddTodo = createMutation({
mutationFn: async (variables) => {
return new Promise((resolve) => {
setTimeout(resolve, 1000)
})
},
use: [mutationMiddleware],
})
Anyway even if I suppress the ts error, the variables from the middleware are overriden with variables from the mutate:
const todo = useAddTodo()
todo.mutate('hello') // this is what `mutationFn` gets. { foo: 'bar' } is dropped.
This will allow a user to pass a select option to createQuery
that will be passed to useQuery
.
Hi, thanks for this amazing library!
I'm curious how to use skipToken
functionality with createQuery
function options.
This is pretty useful for refetch
preventing if we have our query disabled.
React Query just released v5 beta with new features and updated types.
How do you plan on updating this library? I'm willing to help
undefined
is not allowed to be returned as a valid response from a query in react-query
Must return a promise that will either resolve data or throw an error. The data cannot be undefined.
https://tanstack.com/query/latest/docs/react/reference/useQuery#:~:text=Must%20return%20a%20promise%20that%20will%20either%20resolve%20data%20or%20throw%20an%20error.%20The%20data%20cannot%20be%20undefined.
It would be great if the type system restricted undefined from being returned to avoid hitting an error at runtime.
e.g. This should throw a type error about returning undefined
createQuery<number | undefined>({
primaryKey: 'key',
queryFn: () => 1
})
"react-query-kit": "1.4.5"
"@tanstack/react-query": "4.29.19"
When using createQuery
, if I only pass a function (e.g., getAccessToken()
that returns the latest token) as variable to queryFn
, then the resulting query does not refresh when the component refreshes (e.g., due to state change).
This problem occurs if the query is initilally enabled: false
, and later enabled.
import React, { createContext, useContext, useState } from "react";
import ReactDOM from "react-dom";
import {
useQuery,
QueryClient,
QueryClientProvider
} from "@tanstack/react-query";
import { createQuery } from "react-query-kit";
import "./styles.css";
class Data {
constructor(data) {
this.data = data;
}
getData = () => {
return this.data;
};
}
const useMyQuery = createQuery({
primaryKey: "my-query",
queryFn: ({ queryKey }) => {
console.log(
">>> [useMyQuery.queryFn] data =",
queryKey[1].data,
", getData._version =",
queryKey[1].getData._version,
", getData() =",
queryKey[1].getData()
);
return "don't care";
}
});
const WrapperContext = createContext({});
let version = 0;
const WrapperProvider = ({ children }) => {
const [data, setData] = useState(new Data(""));
const getData = () => {
return data.getData();
};
getData._version = version++;
return (
<WrapperContext.Provider
value={{
data,
getData,
setData
}}
>
{children}
</WrapperContext.Provider>
);
};
let counter = 0;
const Component = () => {
const { data, getData, setData } = useContext(WrapperContext);
const query = useQuery({
queryKey: ["todos", getData],
queryFn: () => {
console.log(
">>> [useQuery.queryFn] getData._version =",
getData._version,
", getData =",
getData()
);
return "don't care";
},
cacheTime: 0,
enabled: data.data !== ""
});
const myQuery = useMyQuery({
// Option 1: this print stale data
variables: {
getData: data.getData
},
// Option 2: this prints up-to-date data
// variables: {
// data,
// getData: data.getData,
// },
cacheTime: 0,
enabled: data.data !== ""
});
const onClick = (e) => {
setData(new Data(counter++));
};
return (
<div>
<h2>data = {data.data}</h2>
<button onClick={onClick}>Update data</button>
</div>
);
};
// Create a client
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<WrapperProvider>
<div className="App">
<Component />
</div>
</WrapperProvider>
</QueryClientProvider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Code in Option 1
and Option 2
should give the same result.
Code in Option 1
gives stale value. Option 2
adds an object data
to the variables, and it prints the up-to-date value.
First of all, thank you for the amazing package! However I came accross a situation where I have to create "duplicate" query for one pagination endpoint. One for regular query, and one for infinite query. I think we can add a way to convert InfiniteQuery to a Regular Query (or vice versa)
const useInfinitePosts = createInfiniteQuery<Response, Variables, Error>({
...
})
const usePosts = createQueryFrom(useInfinitePosts, { /* some options here */ })
// and vice versa from query -> infinite query
// or maybe something like this
const usePosts = createCombinedQuery({...})
...
function Component() {
const postQuery = usePosts.query()
const postQueryInfinite = usePosts.infiniteQuery()
}
Although honestly, I don't know enough about the implementation detail to know if it's possible or not. So just a suggestion! Thank you for the awesome work.
I'm using tanstack query v5 rc in my project but v4 is being installed within react-query-kit and causing errors in my setup. How can I get it to install v5 as a peer dependency?
running npm ls --all
gives this
โโโ [email protected]/
โโโ @tanstack/[email protected]/
โโโ @tanstack/[email protected]
react-query-kit
already provides 3 util types:
export type inferVariables<T> = ...;
export type inferData<T> = ...;
export type inferFnData<T> = ...;
Would be great to also provide inferOptions
type, that would return QueryHookOptions
or MutationHookOptions
of the given hook.
You can kinda achieve similar result manually by doing:
type Options = QueryHookOptions<
inferData<typeof usePost>,
any,
inferVariables<typeof usePost>,
any
>;
or
type Options = MutationHookOptions<
inferData<typeof usePost>,
any,
inferVariables<typeof usePost>,
any
>;
But it's not super convenient.
// Define custom hook
const usePost = createQuery<Response, Variables, Error>({
primaryKey: '/posts',
})
// Infer it's options
type usePostOptions = inferOptions<typeof usePost>
// Extend your hook
const useExtendedPost = (options: usePostOptions) => usePost({ ...options, myExtraParam: true })
v2.0.4 introduced automatic Duplicated primaryKey
warning.
I call createQuery
and createSuspenseQuery
in the same file with the same options to create 2 versions of the same hook.
It allows me to have both useQuery
and useSuspenseQuery
versions too choose from, depending on the usage context.
Since they use the same queryFn
, call the same endpoint with the same params, and useSuspenseQuery
is kinda just a syntactic sugar for useQuery({ suspense: true })
, I don't think they need to have different primaryKey
.
After updating too v2.0.4 I started too see a lot off Duplicated primaryKey
warnings.
I assume they are false-positive for my setup?
Is it possible to suppress this console warning? Perhaps, by passing some new prop to options.
Also, I see Duplicated primaryKey
warning a lot during a Next.js fast recompile when working localy. But it's probably unrelated.
Greetings! I've been evaluating your library for use in a future project and think it's an awesome tool for adding structure to TanStack Query. Thank you for open sourcing it and sharing it with the world!
I'm particularly interested in using the router
pattern you've created; however, one limitation I see is that I wouldn't be able to pass in an Array of Strings into the router
method to be able to do something like ['user', 'some-id-123', 'post']
to scope it and make cache invalidation easier to work with for a given user. Would you support adding a keyPrefix
option so that the exported object would still be { post: { ... } }
but the inner key used by TanStack Query is prefixed?
Lines 56 to 61 in bc94ef3
export const router = <TConfig extends RouterConfig>(
key: string,
config: TConfig,
keyPrefix?: string[]
): CreateRouter<TConfig> => {
if (!keyPrefix) {
return buildRouter([key], config)
}
const router = buildRouter([...keyPrefix, key], config)
return keyPrefix.reduce((acc, key) => acc[key], router);
}
How can I add token inside useDefaultOptions
? I tried to do it same with createQuery but got error.
export const useCreatePost = createMutation<TPost, TCreatePostVariables, ApiServiceError>({
mutationFn: (variables) => post.createPost(variables.payload, variables.authToken),
useDefaultOptions: () => {
const {authToken} = useAuth()
return {};
},
})
export const useMarketItemQuery = createQuery<
MarketItem,
{ id: number | undefined }
>({
primaryKey: "/products",
queryFn: async ({ queryKey: [primaryKey, variables] }) => {
return (await http.post.default(`${primaryKey}/${variables.id}`)).data;
},
enabled: (_, { id }) => !!id,
});
type a = ReturnType<typeof useMarketItemQuery>;
type b = a["data"];
// b = unknown
const query = useMarketItemQuery({ variables: { id: 1 } });
type a = typeof query;
type b = a["data"];
// b = MarketItem | undefined
ReturnType should infer valid return type, or there should be helper type for it.
+I acknowledge that there is helper type for inferring TData, but I need the whole valid return type.
Hi there! I was trying out react-query-kit
to see if we can adopt it at work, but had a hard time understanding what's the difference between RQK's primaryKey
and RQ's queryKey
, and why the former does not admit an array as the latter.
Why is this?
Hi,
I've a custom hook called useLoggedInUser
import { createQuery } from "react-query-kit";
import { client } from "../common";
import { User } from "./types";
import { AxiosError } from "axios";
type variables = void;
type Response = User;
export const useLoggedInUser = createQuery<Response, variables, AxiosError>({
primaryKey: "users/me",
queryFn: async () =>
client({
url: "v1/users/me",
method: "GET",
}).then(response => response.data),
enabled: false,
});
I need to call this query only after one of my mutation, and i need to set the data from useLoggedInUser
query to set to my state.
logIn(data, {
onSuccess: ({ accessToken, refreshToken }) => {
signIn({ access: accessToken, refresh: refreshToken });
// I need to call my `useLoggedInUser` here and use the `onSuccess` call back to set data to my sa
},
onError: error => {
showMessage({
message: (error.response?.data as any).message,
type: "danger",
});
},
});
basically i'm trying to call the v1/users/me
endpoint once the login is successful.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.