Sossenbinder
Sossenbinder

Reputation: 5302

React-Query, useQuery is delivering stale data

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?

Upvotes: 0

Views: 2570

Answers (3)

Sossenbinder
Sossenbinder

Reputation: 5302

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

Upvotes: 4

emeraldsanto
emeraldsanto

Reputation: 4741

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

Upvotes: -1

Cybershadow
Cybershadow

Reputation: 1167

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

        }
    })

Upvotes: 0

Related Questions