Giter VIP home page Giter VIP logo

react-query-kit's People

Contributors

liaoliao666 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

react-query-kit's Issues

IDE annotation warnings

I'm not using typescript however PhpStorm gives some annotation warnings such as.

I'm not sure if this is normal.

image

image

Errors thrown in the query function are not handled properly.

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>
    )}
  )
}

Global Error Type

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

useSuspenseQuery error updating 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?

Update `peerDependencies` config to properly accept `@tanstack/react-query` v5

Issue Description

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

Proposed Solution

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.

Idea/Suggestion - Enabled param

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())
  },
})

TFnData bug when defining a global custom error type

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

Expose queryKeyHashFn

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())
  },
})

Cannot update query key in middleware function

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

Few questions and ideas

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:

  1. When 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
  1. I think 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) 
    }
  })
})
  1. I like the shorthand 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.

  1. It's minor but what's the logic of exposing 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.

InvalidateQueries on success without useDefaultOptions

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)

Error: queryFn is not supported, please use fetcher instead

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

mutate requires arguments if middleware is used

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.

Add missing `CreateMutationOptions` type export

Describe your problem

In /src/index.ts file, you have exports of CreateQueryOptions, CreateInfiniteQueryOptions, CreateSuspenseQueryOptions and CreateSuspenseInfiniteQueryOptions types.
image

But CreateMutationOptions export is missing.
image

Consider:

  1. Adding CreateMutationOptions to named exports
  2. Or swapping all named exports with export * from "./types";

setData and middleware

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?

[BUG] Data type with initialData is undefined

Steps to reproduce:

  1. create useAny
  2. use initialData option
  3. useAny in application

Expected result:

const any = useAny()
------^ infered as Response type

Actual result:

any is typeof Response | undefined

Reproduce link and images:

https://codesandbox.io/s/reproduce-react-query-kit-data-type-with-initialdata-is-undefined-8f886n

Defining

image

Usage

image

Comment:

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
   */
}

Best practice for injecting hook returns

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);
};

onError function of Mutation has mismatched types

Issue

In my use Mutation router, I found out the Error and Variable types are swapped.
image

Replication

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;
}

Make expose methods available in middleware

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.

Cannot export hook because QueryHook is a private interface

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.

Primary Key should accepts arrays

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,
}

createQueryClient function

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.

[Feature request] Allow nesting routers

Describe the issue you have

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.

Example

Imagine, if my backend has these endpoints available:

  • GET /api/users
  • POST /api/users
  • POST /api/users/command/report
  • POST /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();

Proposed solution

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

Callbacks in mutation definition vs. inside of component

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

Infered mutation options has invalid `variables` type

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 };

But when used as an option in hook its type becomes Error
image

[Help] Get an error when use select options

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;
    }
  }
);

sandbox https://codesandbox.io/s/example-react-query-kit-load-more-infinite-scroll-forked-qkzrju?file=/services/projects.ts

Variables from useDefaultOptions are not passed to queryFn

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.

Using queryClient in middleware

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
    )
  }
}

Mutation key from variable

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:

  1. add 'useVariablesAsKey' option and if set true, internally append the variable to the key or...
  2. let 'mutationKey' accept functions and inject variable as parameter or...
  3. change 'useDefaultOptions' return to accept 'mutationKey' (currently mutationKey can't be returned)

How to pass any data from middleware to mutationFn?

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.

React Query v5

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

Restrict query response type to not include undefined

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
})

createQuery with only function as variable causes stale state

  • "react-query-kit": "1.4.5"
  • "@tanstack/react-query": "4.29.19"

Description

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.

Example code (sandbox link)

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);

Expected

Code in Option 1 and Option 2 should give the same result.

Actual

Code in Option 1 gives stale value. Option 2 adds an object data to the variables, and it prints the up-to-date value.

Add a way to convert InfiniteQuery to Regular Query (or vice versa)

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.

[Feature Request] Add new `inferOptions` util Typescript type

Context

react-query-kit already provides 3 util types:

export type inferVariables<T> = ...;
export type inferData<T> = ...;
export type inferFnData<T> = ...;

Describe your proposal

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.

Example of possible usage

// 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 }) 

Suppress `Duplicated primaryKey` console warning

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.

Ability to pass `string | string[]` into Router as the key?

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?

export const router = <TConfig extends RouterConfig>(
key: string,
config: TConfig
): CreateRouter<TConfig> => {
return buildRouter([key], config)
}

Proposed Modificaiton

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 add token inside `useDefaultOptions` in `createMutation`

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 {};
  },
})

ReturnType of query instance doesn't match actual valid return value

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.

What's the difference between primaryKey and queryKey?

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?

How to manually trigger a query and get onSuccess callback ?

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.