amplitude / redux-query

A library for managing network state in Redux
https://amplitude.github.io/redux-query
Other
1.1k stars 67 forks source link

Mutation Promise chain setup #199

Closed robertn702 closed 4 years ago

robertn702 commented 4 years ago

Hi, I have the exact same scenario as this issue from 2017, where I'd like to fire two mutations successively. However, I've had difficulty getting the first request to return a promise with the result. I've tried using both mutateAsync and the useMutation hook, both of which have successfully sent the mutation request, but return undefined.

Two questions:

  1. Is it still expected that dispatch(mutateAsync(queryConfig)) and const [,mutation] = useMutation(); mutation(); should return a promise containing the response?
  2. If so, is there a setup step I may have missed that is causing them to return undefined?

Any help would be appreciated.

Edit: Note: I've also read the Redux Actions section of the docs without luck.

rctbusk commented 4 years ago

Could you post an example of this happening with codesandbox?

Might be easier to debug and help you out

robertn702 commented 4 years ago

I've narrowed it down to my middleware. I'm using an implementation similar to what was advised here: https://github.com/amplitude/redux-query/issues/120 in order to add a bearer token to the request. The requests work fine, but something about this breaks the promise chaining.

import { AnyAction, Middleware } from "redux";
import { getToken } from "../helpers/requestUtil";

// add auth token to redux-query requests
const authMiddleware: Middleware = (/* store */) => (next) => (action: AnyAction) => {
  if (
    (action?.type === "@@query/MUTATE_ASYNC" || action?.type === "@@query/REQUEST_ASYNC") &&
    action?.meta?.includeToken // if includeToken is true, add token to the request
  ) {
    getToken().then((token) => {
      const options = action.options || {};
      const headers = options.headers || {};
      const updatedAction = {
        ...action,
        options: {
          ...options,
          headers: {
            ...headers,
            Authorization: `Bearer ${token}`,
          },
        },
      };
      next(updatedAction);
    });
  } else {
    next(action);
  }
};

export default authMiddleware;
robertn702 commented 4 years ago

Just figured this out.

If anyone else comes across this, returning the the next(action) and containing promise solved the issue:

import { AnyAction, Middleware } from "redux";
import { getToken } from "../helpers/requestUtil";

// add auth token to redux-query requests
const authMiddleware: Middleware = (/* store */) => (next) => (action: AnyAction) => {
  if (
    (action?.type === "@@query/MUTATE_ASYNC" || action?.type === "@@query/REQUEST_ASYNC") &&
    action?.meta?.includeToken // if includeToken is true, add token to the request
  ) {
    // add return
    return getToken().then((token) => {
      const options = action.options || {};
      const headers = options.headers || {};
      const updatedAction = {
        ...action,
        options: {
          ...options,
          headers: {
            ...headers,
            Authorization: `Bearer ${token}`,
          },
        },
      };
      // add return
      return next(updatedAction);
    });
  } else {
    // add return
    return next(action);
  }
};

export default authMiddleware;