0
votes

I'm just trying out react-query for the first time, and I've now ran into something I am getting pretty frustrated with.

Following code:

const userSettingsQueryClientKey = "userSettings_queryClient";

export const UserSettingsContextProvider: React.FC = ({ children }) => {

    const queryClient = useQueryClient();

    const { data } = useQuery(userSettingsQueryClientKey, async () => {
        const request = new GetRequest<UserSettings>("/UserSettings/Get");
        const result = await request.get();
        return result.payload;
    });

    const { mutateAsync } = useMutation(async (settings: UserSettings) => {
        const updateRequest = new VoidPostRequest<UserSettings>("/UserSettings/Update");
        await updateRequest.post(settings);
        return settings;
    }, {
        onSuccess: (newData) => {
            queryClient.setQueryData(userSettingsQueryClientKey, newData);
        }
    })

    return (
        <UserSettingsContext.Provider value={{
            settings: { ...data },
            updateSettings: async (settings: UserSettings) => {
                await mutateAsync(settings);
            },
        }}>
            {children}
        </UserSettingsContext.Provider>
    );
}

I am having lots of problems getting the

queryClient.setQueryData(userSettingsQueryClientKey, newData);

part to work.

I have properly debugged my code several times now. During the flow of my app:

  1. The settings are initialized to { useOldVersion: true }

  2. At some point in time, a user will click a checkbox, and call updateSettings with { useOldVersion: false }

  3. The mutation properly posts this update to the server

  4. The onSuccess is called, properly setting the query state to the new value.

However, after the onSuccess the const { data } I'm getting from my useQuery call still delivers the old, stale data. I verified all the previous steps with the debugger, and now I'm really not sure where I'm going wrong.

Any advice how to properly get the new state from the useQuery call?

3
Your code looks okay, updates from mutation responses are a valid concept, see: react-query.tanstack.com/guides/updates-from-mutation-responses some minor things are: mutateAsync needs try/catch, use mutate unless you need promise chaining. I've never put data from react-query into context, as you can just call useQuery (or a custom hook abstraction) when you need the data. It will also keep the data up to date because it triggers refetches when a new subscriber mounts. Can you make a codesandbox reproduction that shows your issue? I can assure you that updates after mutations do work - TkDodo

3 Answers

1
votes

I just found my very stupid bug. All the answers were helpful, but could never find the problem.

My app looked like this:

export default function App() {

  const queryClient = new QueryClient();

  return (
    <QueryClientProvider client={queryClient}>
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        <MyComponent />
        <h2>Start editing to see some magic happen!</h2>
      </div>
    </QueryClientProvider>
  );
}

This meant that the query client is constantly regenerated.

It works when it's moved out like this, so the value is persisted:

const queryClient = new QueryClient();

export default function App() {

  return (
    <QueryClientProvider client={queryClient}>
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        <MyComponent />
        <h2>Start editing to see some magic happen!</h2>
      </div>
    </QueryClientProvider>
  );
}
0
votes

You can try using refetchQueries method exposed by QueryClient. Your code would look something like:

 const { mutateAsync } = useMutation(async (settings: UserSettings) => {
        const updateRequest = new VoidPostRequest<UserSettings>("/UserSettings/Update");
        await updateRequest.post(settings);
        return settings;
    }, {
        onSuccess: (newData) => {
            queryClient.setQueryData(userSettingsQueryClientKey, newData);
            queryClient.refetchQueries(userSettingsQueryClientKey);// this is new

        }
    })

-1
votes

The recommended way to notify the query has changed is through the use of queryClient.invalidateQueries.

You can see an example in the RQ docs.

const { mutateAsync } = useMutation(
  async (settings: UserSettings) => {
    const updateRequest = new VoidPostRequest<UserSettings>("/UserSettings/Update");
    await updateRequest.post(settings);
    return settings;
  },
  {
    onSuccess: (newData) => {
      queryClient.setQueryData(userSettingsQueryClientKey, newData);
      queryClient.invalidateQueries(userSettingsQueryClientKey);
    }
  }
);