3

I don't really know how to ask clearly but, I will paste my code first and ask below.

function useToDos() {
  const queryCache = useQueryCache();
  const fetchTodos = useQuery(
    'fetchTodos',
    () => client.get(paths.todos).then(({ data }: any) => data),
    { enabled: false }
  );

  const createTodo = async ({ name ) =>
    await client.post(paths.todos, { name }).then(({ data }) => data);

  return {
    fetchTodos,
    createTodo: useMutation(createTodo, {
      onMutate: newItem => {
        queryCache.cancelQueries('fetchTodos');
        const previousTodos = queryCache.getQueryData('fetchTodos');
        queryCache.setQueryData('fetchTodos', old => [
          ...old,
          newItem,
        ]);
        return () => queryCache.setQueryData('fetchTodos', previousTodos);
      },
    }),
  };
}

As you can see, I am trying to create my own custom hooks that wrap react-query functionality. Because of this, I need to set my fetchTodos query to be disabled so it doesn't run right away. However, does this break all background data fetching?

Specifically, when I run createTodo and the onMutate method triggers, I would ideally like to have the fetchTodos query update in the background so that my list of todos on the frontend is updated without having to make the request again. But it seems that with the query initially set to be disabled, the background updating doesn't take effect.

As I don't think wrapping react-query hooks into a library of custom hooks is a very great idea, I will probably have more questions about this same setup but for now, I will start here. Thank you. 😊

2 Answers 2

6

The mutation does not automatically triggers a refetch. The way to achieve this using react-query is via queryCache.invalidateQueries to invalidate the cache after the mutation. From the docs:

The invalidateQueries method can be used to invalidate and refetch single or multiple queries in the cache based on their query keys or any other functionally accessible property/state of the query. By default, all matching queries are immediately marked as stale and active queries are refetched in the background.

So you can configure the useMutation to invalidate the query when the mutation settles. Example:

function useToDos() {
  const queryCache = useQueryCache();
  const fetchTodos = useQuery(
    'fetchTodos',
    () => client.get(paths.todos).then(({ data }: any) => data),
    { enabled: false }
  );

  const createTodo = async ({ name ) =>
    await client.post(paths.todos, { name }).then(({ data }) => data);

  return {
    fetchTodos,
    createTodo: useMutation(createTodo, {
      onMutate: newItem => {
        queryCache.cancelQueries('fetchTodos');
        const previousTodos = queryCache.getQueryData('fetchTodos');
        queryCache.setQueryData('fetchTodos', old => [
          ...old,
          newItem,
        ]);
        return () => queryCache.setQueryData('fetchTodos', previousTodos);
      },

      onSettled: () => {
        cache.invalidateQueries('fetchTodos');
      }

    }),
  };
}
Sign up to request clarification or add additional context in comments.

6 Comments

What is the difference between invalidateQueries and refetchQueries?
Look like invalidateQueries is just a better option of refetchQueries: github.com/tannerlinsley/react-query/issues/…
No, actually, refetch will force the queries to fetch no matter what, whereas invalidate only push fetching when the queries are not being used. Sometimes we want either. They all have their own place.
Thank you SO MUCH for your last comment that's not very clear in their doc indeed!
Curious, why invalidate onSettled instead of onSuccess?
|
0

What about splitting the logic into two different hooks? Instead of a monolith like useToDos?

That way you could have a hook for fetching:

const fetchData = _ => client.get(paths.todos).then(({ data }: any) => data)

export default function useFetchTodo(
  config = {
    refetchOnWindowFocus: false,
    enabled: false
  }
) {
  return useQuery('fetchData', fetchData, config)
}

And in your mutation hook you can refetch manually, before createTodo

import useFetchTodo from './useFetchTodo'
//
const { refetch } = useFetchTodo()

// before createTodo
refetch()

1 Comment

Interesting setup. I am curious though why call refetch before creating a new item? From my understanding, if the query isn't disabled, whenever I'd create a new item, that query cache would update with it and my list of items in the UI would be updated. Am I missing something? Would I need to call refetch regardless (unless doing optimistic responses)?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.