reduxjs / redux-toolkit

The official, opinionated, batteries-included toolset for efficient Redux development
https://redux-toolkit.js.org
MIT License
10.74k stars 1.18k forks source link

Server side mutations not populating hook data purely server side #3647

Closed eugeniosegala closed 1 year ago

eugeniosegala commented 1 year ago

Hey!

I'm using RTK Query server side with Next JS.

I may have found a potential bug related to how the data are sent from server to client, within the context of mutations.

I have executed the following mutation server side (inside getServerSideProps):

await store.dispatch(
  saveForm.initiate({
    firstName,
  })
);

await Promise.all(store.dispatch(api.util.getRunningQueriesThunk()));

Then, when I get the data client side:

const data = useSaveFormMutation();

The content of data is always:

[null,{"status":"uninitialized","isUninitialized":true,"isLoading":false,"isSuccess":false,"isError":false}]

The above is happening only when JavaScript is disabled. However, this is working fine with normal queries such as useGetFormQuery.

It looks like there is a difference in how the data flows from server to client between queries and mutations.

I'm happy to help fixing this problem but I wanted to collect feedback first.

Thanks!

EskiMojo14 commented 1 year ago

Mutations don't share data unless you use a fixedCacheKey. (you'd also need to use getRunningMutationsThunk instead)

eugeniosegala commented 1 year ago

Thanks for the help @EskiMojo14

So the final code will be like this?

const FormOne = () => {
  const data = useSaveFormMutation({
    fixedCacheKey: "shared-update-form",
  });

  return (
    <div>
      {JSON.stringify(data)}
      <form method="POST">
        <Input
          label={{
            children: "First Name",
          }}
          type="text"
          name="firstName"
        />
        <Button>Continue</Button>
      </form>
    </div>
  );
};

interface ExtendedNextApiRequest extends NextReq {
  body?: {
    firstName: number;
  };
}

export const getServerSideProps = nextServerStore.getServerSideProps(
  (store) =>
    async ({ req }: { req: ExtendedNextApiRequest }) => {
      const firstName = req.body?.["firstName"];

      // dispatching Redux actions server side
      if (req.method === "POST") {

        // init REST saveForm SSR mutation
        await store.dispatch(
          saveForm.initiate({
            firstName,
          })
        );

        await Promise.all(store.dispatch(api.util.getRunningMutationsThunk()));

        return {
          props: {},
        };
      }

      return {
        props: {},
      };
    }
);

export default FormOne;

I basically replaced getRunningQueriesThunk with getRunningMutationsThunk and I added the fixedCacheKey to useSaveFormMutation.

Do I also need to add the key server side as well?

EskiMojo14 commented 1 year ago

yep

await store.dispatch(
  saveForm.initiate(
    { firstName },
    { fixedCacheKey: "shared-update-form" }
  )
);
eugeniosegala commented 1 year ago

Nice! It worked pretty well! Thanks @EskiMojo14 🙏

Just of curiosity, will the fixedCacheKey be destroyed from the server after the response is returned? (asking from a memory consumption prospective).

EskiMojo14 commented 1 year ago

No - by using fixedCacheKey you opt out of the cache for that mutation being cleaned up automatically. You would need to use the reset function returned from the hook to clear it.