Giter Site home page Giter Site logo

redux-axios-middleware's People

Contributors

alexeib avatar andrefgneves avatar art049 avatar crobinson42 avatar danjersoft avatar nmaves avatar palatnyi avatar pkellner avatar rannn505 avatar svrcekmichal avatar thenerdyhamster avatar vasilevich avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

redux-axios-middleware's Issues

Interceptor Response not respond to anything

Hi, Thanks for this great package!. Need ask or maybe I doing it wrong.

I'm trying to setup the axiosmiddleware with interceptor. The interceptor request is working properly but the interceptor response not working.

here is my code:


function interceptorRequest(dispatch, config) {
    const token = cookie.load('_t');
    const configuration = config;
    if (token) {
        configuration.headers.Authorization = `Bearer ${cookie.load('_t')}`;
    }

    return configuration;
}

function interceptorResponse(dispatch, response) {
    console.log(dispatch);
    console.log(response);
    console.log(error);
}

export const client = axios.create({
    baseURL: API_URL,
    headers: {
        Accept: 'application/json',
    },
});

export const clientOptions = {
    interceptors: {
        request: [interceptorRequest],
        response: [interceptorResponse],
    },
};

// store.js

// other import
import { client, clientOptions } from '../utils/axios';


const reduxRouterMiddleware = routerMiddleware(history);

const middlewares = [
    thunk,
    axiosMiddleware(client, clientOptions),
    reduxRouterMiddleware,
];

const store = compose(applyMiddleware(...middlewares))(createStore)(reducer);

export default store;

my question:

  1. my console.log (at response interceptor) never return anything. Why it not return anything?
  2. how can I setup the interceptors with error? For example:
// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });

Thanks!

set token

I'm using your middleware. But, when I use the action it does not work, says no token:

import axios from "axios";
import Cookie from "js-cookie";
import { API_BASE_URL } from '../config';

let token = Cookie.get('token') ? Cookie.get('token') : '';

export const api = axios.create({
    baseURL: API_BASE_URL,
    headers: {'Authorization': token}
});

export default api;

action:

export function userInfo() {
    return {
        type: types.USER_INFO,
        payload: {
            request: {
                url: `/user-info/`,
                method: "get"
            }
        }
    };
}

in the login component I put the Cookie and go to the main page:

Cookie.set('token', 'Token '+ response.payload.data.token);
 this.context.router.history.push('/');

And already on the main page do not work action. I can see Cookies in Aplication -> Cookies.
Only after rebooting the page everything works.

Can have to restart middleware config?
Thanks :)

Examples repository

Hello,

I recently created a repository which demonstrate how to use redux-axios-middleware using a typescript/react frontend and a .NET Core backend.

I think it could be interesting to give a link to this repository to people who want to see how it looks with a pure example. What do you think?

Please take a look here : https://github.com/Odonno/react-redux-axios-middleware-netcore-example

PS : More examples are coming but you can raise an issue if you see any mistake or if you have any suggestion.

Intercept response error without handling it

I would like to be able to intercept a response error with a response error interceptor and still get the ${requestAction}_FAIL action to be triggered. But it seems that the error just gets swallowed up whenever I add such an interceptor. Am I missing something?

For the moment, I'm just using onError handler like this:

const middlewareConfig = {
  onError: ({ action, error, next, dispatch }) => {
    if (error.response.status === 401) {
      dispatch(AuthActions.logout());
    }
    // propagate failure for further handling
    const nextAction = {
      type: `${action.type}_FAIL`,
      error,
      meta: {
        previousAction: action,
      },
    };
    next(nextAction);
    return nextAction;
  },
};

Action as promise

This issue/question arose from my previous #67. I struggle to get the action returned as a promise. As there is no example that I know of, I wanted to ask how it should be configured properly. right now I have (using typescript)

export const actionCreators = {
    setCurrentRole: (id: number): AppThunkAction<KnownAction> => (dispatch, getState) => {
        dispatch(new SetCurrentRole({ request: { url: `role/${id}/setCurrent`, method: "PUT" } }));
    }
};

And then on my button I have

onClick={(e) => this.props.setCurrentRole(5)}

This however returns undefined (as it seems to be void function) not a promise as hinted by documentation

Props are injected by redux-connect. Code just for completness sake

export default connect<{
    roles: RolesStore.RolesState
},
    typeof RolesStore.actionCreators, SidebarAccountPropsCustom>(
    (state: ApplicationState) => ({
        roles: state.roles
    }), // Selects which state properties are merged into the component's props
    RolesStore.actionCreators                 // Selects which action creators are merged into the component's props
    )(SidebarAccount);

These are my middlewares:

var middlewares: Middleware[] = [axiosMiddleware(client, axiosMiddlewareOptions), thunk, typedToPlain];
    if (windowIfDefined) {
        const loggerMiddleware = (createLogger as any)();
        middlewares.push(loggerMiddleware);
        if (history) {
            middlewares.push(routerMiddleware(history));
        }
    }

axiosMiddlewareOptions just include setup for authentication.

Hope to get to the bottom of this, cheers!

Change default actions in middleware

I would like to simplify little bit default actions onSuccess and onError. My suggestion is:

export const onSuccess = ({ action, next, response }, options) => {
  const nextAction = {
    type: getActionTypes(action, options)[1],
    payload: response,
    meta: {
      previousAction: action
    }
  };
  next(nextAction);
  return nextAction;
};

PROS:

  • all response is at one place, and we are using default axios response, so people using axios know where to find what

This is the proposed error handler:

export const onError = ({ action, next, error }, options) => {
  let errorObject;
  if(error instanceof Error) {
    errorObject = {
      data: error.message,
      status: 0
    };
    if(process.env.NODE_ENV !== 'production') {
      console.log('HTTP Failure in Axios', error);
    }
  } else {
    errorObject = error;
  }
  const nextAction = {
    type: getActionTypes(action, options)[2],
    error: errorObject,
    meta: {
      previousAction: action
    }
  };

  next(nextAction);
  return nextAction;
};

Error handler will handle HTTP Failure and create generic action error if something bad occured. It will also handle logging to console if not production env is used. After little bit of googling I found that code 0 may represent various http failures.

PROS:

  • everyone can handle Error object in onError handler whatever he like
  • default error response of axios is used and returned

No action after request finished.

When i dispatch action with type and request everything seems ok. I am transforming request with transformRequest and after request finish onSuccess and onError methods working well. But there is no action fires with '_SUCCESS' or '_ERROR' suffix. What could be my mistake?

return {
        type: types.POST_LOGIN,
        payload: {
            request: {
                url: '/users/login',
                method: 'POST',
                data: { username, password }
            }
        }
    }


onSuccess: function (app) {

        if (!app.response.data.Response && app.response.data.Header && app.response.data.Header.errors) {
            return app.dispatch(actions.apiError(app.response.data.Header.errors));
        }

        app.dispatch(actions.ajaxReponse(app.response.data));
    }

action Object {type: "AJAX_RESPONSE", data: Object}

Uncaught TypeError: (0 , _reduxAxiosMiddleware.isAxiosRequest) is not a function

isAxiosRequest works with import { isAxiosRequest } from 'redux-axios-middleware/lib/defaults';
Neither working with import { isAxiosRequest } from 'redux-axios-middleware/src/defaults';.
Nor with import { isAxiosRequest } from 'redux-axios-middleware/dist/bundle';
Also tried with import axiosMiddleware from 'redux-axios-middleware'; and then axiosMiddleware.isAxiosRequest(action)

lib directory is ignored in git.
src directory is ignored in npm.

How to use isAxiosRequest?
Failing with isAxiosRequest(action)

Tried version 4.0.0, 3.1.1 and 3.0
May be I am missing something basic!

[Question] how to use this together with redux-thunk?

Hi, I'm currently looking for a way to reduce much duplicate code from my action creators and I think this project could help me.

I'm currently using redux-thunk for all my async action creators. Is it possible to dispatch a standard action when a request fails?

add webpack build for UMD

Can you implement or will you accept a PR adding a webpack build into the package for UMD support? This would be great for your package, for example, I wanted to realtime code demo the features today to a client using Cloud9 or Kobra.io but couldn't use this library with a script tag via https://unpkg.com/[email protected]/ because it's only built for commonJs.

Dispatch new action when async action is succeeded.

Lets say that i have login action like following.

export function login(credentials) {
	return {
		types: ['LOGIN','LOGIN_SUCCESS','LOGIN_ERROR'],
		payload: {
		request: {
			url: '/api/login',
			method: 'POST',,
			data: credentials
		}
		}
	}
}

When login is succeeded, i want to dispatch new action doAnother.
How can i do that?

Do i need to do that in reducer when action.type = LOGIN_SUCCESS?
I guess its anti-pattern, and there must be more redux-like approach.

How about an onRequest hook?

Hi

I am working on a notification system for my app and was able to use onError and onSuccess hooks to dispatch additional actions like this:

import {onSuccess as defaultOnSuccess, onError as defaultOnError} from 'redux-axios-middleware/lib/defaults'

export const onSuccess = ({ action, next, response }, options) => {
  if (action.messages && action.messages[1]) {
    let message = action.messages[1]
    if (typeof message == 'function') {
      message = message(action)
    }
    next(notify(message, 'success'))
  }
  return defaultOnSuccess({action, next, response}, options)
};

export const onError = ({ action, next, error }, options) => {
  if (action.messages && action.messages[2]) {
    let message = action.messages[2]
    if (typeof message == 'function') {
      message = message(action)
    }
    next(notify(message, 'error'))
  }
  return defaultOnError({action, next, error}, options)
};

However, I cannot find any kind of onRequest handler to display the loading message. I can add another middleware, but it seems that it would make sense for redux-axios-middleware to have this

setupedClient.client.request is not a function

Hi,

I'm getting ^ that error from::

const axios = require('axios');

module.exports = {
  client: axios.create({
      baseURL: 'http://localhost:5000',
      responseType: 'json',
      // timeout: 5000,
    }),
}

and in my action it looks like this::

export const GET_SHIT = 'GET_SHIT';

export function getShit() {
return  {
    type: GET_SHIT,
    payload: {
      request: {
        method: "get",
        url: "/getShit"
      }
    }       
  }
}

but i get an error that says ::

Uncaught TypeError: setupedClient.client.request is not a function
    at eval (eval at <anonymous> (bundle.js:19090), <anonymous>:210:37)
    at Object.eval [as getValue] (eval at <anonymous> (bundle.js:19930), <anonymous>:4:12)
    at GettingShit.componentDidMount (eval at <anonymous> (bundle.js:7864), <anonymous>:91:18)
    at CallbackQueue.notifyAll (eval at <anonymous> (bundle.js:6336), <anonymous>:76:22)
    at ReactReconcileTransaction.close (eval at <anonymous> (bundle.js:17683), <anonymous>:80:26)
    at ReactReconcileTransaction.closeAll (eval at <anonymous> (bundle.js:1804), <anonymous>:206:25)
    at ReactReconcileTransaction.perform (eval at <anonymous> (bundle.js:1804), <anonymous>:153:16)
    at batchedMountComponentIntoNode (eval at <anonymous> (bundle.js:6432), <anonymous>:126:15)
    at ReactDefaultBatchingStrategyTransaction.perform (eval at <anonymous> (bundle.js:1804), <anonymous>:140:20)
    at Object.batchedUpdates (eval at <anonymous> (bundle.js:17575), <anonymous>:62:26)
(anonymous) @ bundle.js?4fc5:210
(anonymous) @ bindActionCreators.js?d387:3
componentDidMount @ gettingShit.jsx?4b86:55
notifyAll @ CallbackQueue.js?1e54:76
close @ ReactReconcileTransaction.js?e1cc:80
closeAll @ Transaction.js?b216:206
perform @ Transaction.js?b216:153
batchedMountComponentIntoNode @ ReactMount.js?ccff:126
perform @ Transaction.js?b216:140
batchedUpdates @ ReactDefaultBatchingStrategy.js?1670:62
batchedUpdates @ ReactUpdates.js?89d4:97
_renderNewRootComponent @ ReactMount.js?ccff:320
_renderSubtreeIntoContainer @ ReactMount.js?ccff:401
render @ ReactMount.js?ccff:422
(anonymous) @ index.jsx?f515:42
(anonymous) @ bundle.js:7085
__webpack_require__ @ bundle.js:20
(anonymous) @ bundle.js:21865
__webpack_require__ @ bundle.js:20
(anonymous) @ bundle.js:66
(anonymous) @ bundle.js:69

the index file with this stuff is::

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';

import axiosMiddleware from 'redux-axios-middleware';
import promise from 'redux-promise';
import createMemoryHistory from 'history/createMemoryHistory';
import client from './utils/axiosconfig.js';

import store from './reducers/index.js';

const history = createMemoryHistory();

const createStoreWithMiddleware = applyMiddleware(axiosMiddleware(client), promise)(createStore);

ReactDOM.render(
  <Provider store={createStoreWithMiddleware(store)}> 
etc.etc. 

is there something i'm doing wrong?

Why is the response used next by default?

Hello! Many thanks to you for the work.
I have a question. Why is the response used next by default? In this case, the action with the request goes through the chain of middlewares only after redux-axios-middleware. Thus, if we do, for example, the middleware of authentification, we will have to:

  1. Divide the middleware fo authentification into two to process the request and response. Insert them before and after redux-axios-middleware.
  2. Override onSuccess and onError methods in options.
    Why not call dispatch by default?
    Thanks!

getState() is cached no matter that store was changed

As the redux-axios-middleware is caching configuration per name of the client it's not possible different store to be passed for testing unless different client is created for each test which sounds overwhelming.

Here is a sample test that illustrates the problem:

import axios from 'axios';
import axiosMiddleware from '../src/middleware';

import { expect } from 'chai';
import configureMockStore from 'redux-mock-store';
import MockAdapter from 'axios-mock-adapter';

const options = {
  returnRejectedPromiseOnError: true,

  interceptors: {
    request: [
      ({ getState, dispatch }, config) => {
        console.log('state in interceptor: ', getState());
        config.headers['Authorization'] = 'Bearer ' + getState().access_token;
        return config;
      }
    ],
    response: [
      {
        success: ({ dispatch }, response) => {
          return response;
        },
        error: ({ dispatch, getSourceAction }, error) => {
          return Promise.reject(error);
        }
      }
    ]
  }
};

const middleware = axiosMiddleware(axios, options);

describe('axiosMiddleware', () => {
  const mockAxiosClient = new MockAdapter(axios);
  const mockStore = configureMockStore([middleware]);
  const mockAdapter = mockAxiosClient.adapter();

  afterEach(() => {
    mockAxiosClient.reset();
  });

  after(() => {
    mockAxiosClient.restore();
  });

  it('attaches authorization header on each request', () => {
    let got;

    mockAxiosClient.onGet('/test').reply(config => {
      got = config.headers['Authorization'];
      return [200];
    });

    const action = () => {
      return {
        type: 'LOAD',
        payload: {
          request: {
            url: '/test'
          }
        }
      };
    };

    const store = mockStore({ access_token: '::access_token::' });

    return store.dispatch(action()).then(() => {
      expect(got).to.equal('Bearer ::access_token::');
    });
  });


  it('attaches another authorization header on each request', () => {
    let got;

    mockAxiosClient.onGet('/test2').reply(config => {
      got = config.headers['Authorization'];
      return [200];
    });

    const action = () => {
      return {
        type: 'ANOTHER_LOAD_ACTION',
        payload: {
          request: {
            url: '/test2'
          }
        }
      };
    };

    const store = mockStore({ access_token: '::another_access_token::' });

    return store.dispatch(action()).then(() => {
      expect(got).to.equal('Bearer ::another_access_token::');
    });
  });
});

The following test fails with:

  1) axiosMiddleware attaches another authorization header on each request:

      AssertionError: expected 'Bearer ::access_token::' to equal 'Bearer ::another_access_token::'
      + expected - actual

      -Bearer ::access_token::
      +Bearer ::another_access_token::

e.g the test of first test is used as it caches the store instance that was passed no matter that another store is created using mockStore.

As better feedback I've added few loggings in the interceptor too:

return ({ getState, dispatch }) => next => action => {
    if (!middlewareOptions.isAxiosRequest(action)) {
      return next(action);
    }
    console.log(`action: ${action.type}, store value: `, getState());

and this is the execution log:

  axiosMiddleware
action: LOAD, store value:  { access_token: '::access_token::' } -> this is in the middleware
state in interceptor:  { access_token: '::access_token::' } -> this is in the interceptor 
    โœ“ attaches authorization header on each request
action: ANOTHER_LOAD_ACTION, store value:  { access_token: '::another_access_token::' } -> this is in the middleware
state in interceptor:  { access_token: '::access_token::' } -> this is in the interceptor

Making a simple get request and setting the response to payload

Hello,

I'm hitting a bunch of road blocks trying to set a response from a axios.get request to action payload. The main issue is that since the response is a promise it takes the entire promise as the payload and there is no way to set the response within the promise as the payload. Any advice on how to use this middleware to rectify the issue I am having?

I greatly appreciate any direction given and I thank you in advance.

Peter

Action returned by promise not put through middleware

I have a middleware which formats API responses. Is it possible to actions returned by calling .then/.catch on the request action to be put through that middleware?

Example:

action/login.js

export const logIn = (username, password) => {
  const action = {
    types: [LOG_IN, LOG_IN_SUCCESS, LOG_IN_FAIL],
    payload: {
      request: {
        // request data
      }
    },
  };

  return dispatch => dispatch(action);
};


components/LogIn.js

class LogIn extends Component {
  submitForm = (event) => {
    const { logIn } = this.props;
    const { username, password } = this.state;

    event.preventDefault(); 
    logIn(username, password)
      .catch(failAction => console.log(failAction)) // <---
  };

  // render and stuff
}

failAction is "raw" action, meaning it didn't go through the middleware. Is there a possiblity to change that, or is formatting manually each time the only option?

Improve Documentation (example)

I'm struggling to understand the correct way to utilize this library. If I need to parse the returned response and do any sort of extraction/validation ... it seems, I have to handle response logic within the reducers (which doesn't seem right)? Moreover, I notice there are onSuccess/onError functions but no documentation on how or where to define and whether or not they are global on every request or per request (are they part of the middleware config or action). Overall, this needs improved documentation, is there any example application that utilizes this library so I can see if this fits the teams needs or whether to create our own middleware for handling request headers and JWT tokens?

Full example App

Could someone provide a full example App using redux axios middleware?

Where to pass onSuccess, onComplete, etc?

I am trying to have callbacks, but it's not working in the object:

    type: ORDER_SUBMIT,
    onSuccess({ getState, dispatch}){},
    payload: {
      request: {
        method: 'put',
        url: `/tickets/${orderData.orderTicketCode}`,
        data: {}
      },
      onSuccess({ getState, dispatch }) {
 
      },
      meta: {
        onSuccess({ getState, dispatch}) {
        }
      }
    }

Where is the correct location?

Send filtered response to reducer as payload?

Can we influence what the response returns as payload to the action?

For example I'm querying an API that returns a result with way more information that I needed, and I only want to use a single piece. Can I somehow filter after successfully receiving the raw response data, and sending only the data I need to the reducer with the _SUCCESS action?

There is a typo mistake in your example

I don't know if this is something that is related to the latest versions of axios.

But, when you state that:

const client = axios.create({ //all axios can be used, shown in axios documentation
baseUrl:'http://localhost:8080/api',
responseType: 'json'
});

The URL of baseUrl should be all capital letters, e.g. baseURL. This was something that made axios don't find the right property when it tries to check if there's any config parameter available with baseURL while creating the request object.

Best regards

Possible Unhandled Promise Rejection (id: 0)

Hi Guys thanks for the middleware library its really helpful.

Am facing issue when I receive success response from the server.

_FAIL works absolutely fine without error. But,

_SUCCESS is called and response is received as expected, but am getting a warning saying "possible unhandled promise Rejection (id: 1) TypeError: null is not a object (evaluating 'action.type') getStateForAction)"

Is it something you already faced, because promise is generated only by the middleware library for the action am performing.

Here is the config info:

const client = axios.create({ //all axios can be used, shown in axios documentation
    baseURL: 'http://192.168.5.10/CoreServices/',
    responseType: 'json'
});

const options = {
    interceptors: {
        request: [{
            success: ({getState, dispatch, getSourceAction}, req) => {
                if (getState() && getState().sessionReducer && getState().sessionReducer.token) {
                    req.headers['Authorization'] = 'Bearer ' + getState().sessionReducer.token
                }
                dispatch({
                    type: SHOW_LOADER
                });

                return req
            },
            error: ({getState, dispatch, getSourceAction}, req) => {
                dispatch({
                    type: HIDE_LOADER
                });

                return Promise.reject(req);
            }
        }],
        response: [{
            success: ({getState, dispatch, getSourceAction}, res) => {
                store.dispatch({
                    type: HIDE_LOADER
                });
                return res;
            },
            error: ({getState, dispatch, getSourceAction}, res) => {
                store.dispatch({
                    type: HIDE_LOADER
                });
                return Promise.reject(res);
            }
        }]
    }
};


switch (action.type) {
    case `${LOGIN}`:
        return {...state}
        break;
    case `${LOGIN}_SUCCESS`:
        return {...state, isLoggedIn: true}
        break;
    case `${LOGIN}_FAIL`:
        console.log('login failed!');
        if(action.error &&
            action.error.response &&
            action.error.response.data &&
            action.error.response.data.errors &&
            Array.isArray(action.error.response.data.errors) &&
            action.error.response.data.errors.length > 0) {
            return {...state, isLoggedIn: false, error: action.error.response.data.errors[0].message}
        } else {
            return {...state, error: 'test error from backend'}
        }
        break;
    case RESET_AUTHENTICATION_ERROR:
        return {...state, error: null}
        break;
    default:
        return {...state}
}

Please let me know if you have some info about this.

  • Dilip.

Reject (FAIL) action based on response

Have a case where an api do not return the correct error-status with request that fails, so I have added an interceptors in the axios-request

const client = axios.create({
  baseURL: 'https://api.domain.com'
})

client.interceptors.response.use((response) => {
  if (response.data && response.data.status && response.data.status === 'error') {
    return Promise.reject(new Error(response.data.message || 'Request failed'))
  }
  return response
}, (error) => {
  return Promise.reject(error)
})

This works fine with a plain axios, a request that contain a status-fields which is 'error', can be catched in the promise:

client.get('/auth/login', {
  params: {
    username: 'bar',
    passord: 'foo'
  }
}).then((data) => console.log('LOGIN OK', data))
.catch((error) => console.log('LOGIN ERROR', error.message))

With redux-axios-middleware I can get the action to fail with returnRejectedPromiseOnError option in config:

const middleware = [
  axiosMiddleware(client, {
    returnRejectedPromiseOnError: true
  })
]

But I also get a unhandled error in the console (which is not happening with the plain axios-setup)

HTTP Failure in Axios Error: Wrong username or passord.
    at App.js:22
    at tryCallOne (core.js:37)
    at core.js:123
    at JSTimers.js:295
    at _callTimer (JSTimers.js:148)
    at _callImmediatesPass (JSTimers.js:196)
    at Object.callImmediates (JSTimers.js:464)
    at MessageQueue.__callImmediates (MessageQueue.js:282)
    at MessageQueue.js:137
    at MessageQueue.__guard (MessageQueue.js:269)

intercepting: getSourceAction in response is returning undefined

Is this intended to work only for the request ? On my side calling getSourceAction by passing the response is returning undefined. Here is a sample snippet:

response: [
      {
        success: ({ dispatch, getSourceAction }, response) => {
          console.log(getSourceAction(response))
          return Promise.resolve(response)
        },
        error: ({ dispatch, getSourceAction }, error) => {
          console.log(getSourceAction(error))
          return Promise.reject(error)
        }
      }
    ]

What I'm trying to accomplish is refreshing of a token when it expires and server returns that request is not authorized.

Here is an example how this could be accomplished using axios interceptors: https://plnkr.co/edit/0ZLpc8jgKI18w4c0f905?p=preview

Library limitations

Hi. First of all, thanks for the effort that went into this. It's really a neatly done piece of code. Congrats!

Secondly, after using it for a little while, I seem to be starting to hit the limits of the api (maybe) and I was wondering if I am just doing something wrong, or if this is indeed not 100% suited for me.

My usecase:
I have to fire a request and then on success I have a to chain a few more requests after that. e.g. :

  1. Create a resource.
  2. On success an id is returned
  3. Use this id to perform other requests

And here's where I struggle.
Firing action in reducer is obviously an anti pattern so I can't go that way.
Chaining of actions as described in readme would work (in my setup I don't get a promise for some reason so I am only speaking theoretically). The problem with this though is I can create this resource from many places . Chaining actions in UI seems therefore a bit too repetitive for comfort.

Am I missing something or would it make more sense for me to use pure axios with some defaults set?
Main reason why I went with this lib is ease of adding authorization and very neat config on the action side. Wouldn't want to lose that but I feel like I might have hit a limitation.

Thanks for your input!

Update doc

Please update your README from this one:

const client = axios.create({ //all axios can be used, shown in axios documentation
  baseURL:'http://localhost:8080/api',
  responseType: 'json'
});

To this one:

const client = axios.create({ //all axios can be used, shown in axios documentation
  baseURL:'http://localhost:8080/api',
  options: {   
    responseType: 'json'
  }
});

As I see from this line you use options for Axios from client.options. So If I would like to use responseType:json I should put it to payload.options or client.options.

I just spent a lot of time on investigating why I receive the following error:

axios Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'response Type' is '' or 'text' (was 'json').

So the solution was just simple - put responseType:json to client.options.

takeEvery/takeLatest are not catching *_SUCCESS or *_FAIL actions

For some reason when i have an action like USER/LOGIN, i can't do anything like

yield takeEvery('USER/LOGIN_SUCCESS', mySaga);

However,

yield takeEvery('USER/LOGIN', mySaga);

in my saga works just fine, but obviously at the moment request is dispatched, not successfully finished.
I've added a monitoring reducer and USER/LOGIN_SUCCESS is indeed dispatched, but takeEvery doesn't seem to care. Any ideas why?

Why not export the `SUCCESS_SUFFIX` and `ERROR_SUFFIX` in `index.js`?

I noticed in the document the default success and error suffix for action types are mentioned and exported from getActionTypes.js. But in index.js the 2 default suffix constants are not exported.

This makes developers to maintain 2 more hardcode constant in their code. However I wish I could use the default constants rather than hardcode.

Uncaught (in Promise) issue

When the API return an error, return Promise.reject(newAction); this line throw an uncaught exception. Which need to be caught like this store.dispatch(someAsyncAction).catch(err => err) otherwise it will throw an Uncaught (in Promise) error. I think return newAction is good enough there.

return client.request(options.getRequestConfig(action))
      .then(
        (response) => {
          const newAction = options.onSuccess({ action, next, response, getState, dispatch }, options);
          options.onComplete({ action: newAction, next, getState, dispatch }, options);

          return newAction;
        },
        (error) => {
          const newAction = options.onError({ action, next, error, getState, dispatch }, options);
          options.onComplete({ action: newAction, next, getState, dispatch }, options);
          return Promise.reject(newAction);
        });

How i can do cross domain request?

I set "Access-Control-Allow-Origin: *", but always get a warning:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

[feature] caching w/ age param

What do you guys think about adding a caching option for requests? We could implement an option w/ the cache to bypass cache if last request is older than age or something along those lines... thoughts?

Success action is not propagated to other middleware

Hi,

I have a problem with asynchronous request and the succeeding success action not being passed on to the other middleware but being completely consumed by redux-axios-middleware.

I have a custom middleware that is taking care of access token handling and that is saving the access token on LOGIN_SUCCESS to a cookie. But the LOGIN_SUCCESS action does never reach the custom middleware. Can someone pls shed some light here ? From my understanding actions get passed through all middleware, right ?

Thanks for your help,
Alexander

Ability to chain calls

I have used this in the past and it works really well for chaining promises.

redux-promise-middleware

Is is really nice to be able to call a then on your bound action creator to capture the response outside of your reducers. You can see their latest release notes.

I think it might be a really simple change if you want me to create a PR.

How to run unit tests?

Hi,
I am considering using this instead of redux-sagas to manage all my api calls. Sagas makes it easy to test actions and dispatches.

What is the recommended way to test my app when using redux-axios-middleware to make api calls?

thanks,

OAuth2 Implementation

Hi,

I was wondering how I could easily setup an interceptor that adds to requests headers an Authorization token excepted for some (like the one used to retrieve the token, etc..).

I'm asking you this since I couldn't find any way that allows the request intereceptor to get the proper action.

interceptors : {
      request: [({ getState, dispatch, action }, config) => {

No matter what action is performed, only the first one called will persist here (in my case the AUTH action if i'm not already signed in).

I've read on different issue that this is related to the client instance, but honestly I'm totally lost now and I can't figure out what to do.

I'd really appreciate some help!
Thank for your great work too!

Regards,

Error never get into catch section after calling an actions

Hi,

At docs I read at the section

Actions that are handled by this middleware return a promise. This gives you the ability to chain actions. A good example of this might be a form. In the form you might dispatch an actions to store the form values. The normal flow of the action into the reducers would not be altered but you can chain a then/catch onto the initial dispatch.

this.props.saveForm(formData)
  .then(() => {
    // router the user away
    this.context.router.push("/my/home/page")
  })
  .catch((response) => {
    //handle form errors
  })

So , I want to tried with my simple form handler to handle error section

handleFormSubmit(data) {
        this.props
            .signIn(data)
            .then((response) => {

            })
            .catch(response => {
                console.log(response);
            });
    }

reducer:

export function signIn({ email, password }) {
    return {
        type: 'LOGIN',
        payload: {
            request: {
                url: 'auth/login',
                method: 'POST',
                data: { email, password },
            },
        },
    };

I want to tried error response to see if its working properly. But it never console it out (422, 401) response.

But If I move my console.log to then section it will print it out properly.

Object {type: "LOGIN_FAIL", error: Error: Request failed with status code 422, meta: Object}

Why It enter to success response instead of catch section (I removed all my response interceptor)?.

How to call async method

I'm using your great middleware! Thanks guys but:

when:

GameModal.react.js

async doCreateGame() {
    const {
        fields,
        hideModal,
        formDisabled,
    } = this.props;
    
    if (formDisabled) return;
    
    try {
        const response = await createGame(fields.$values());
        console.log(response);
    
    } catch (error) {
        console.log(error);
    }
}

Actions.js

export function createGame(fields) {
	return {
		types: ['CREATE_GAME','CREATE_GAME_SUCCESS','CREATE_GAME_ERROR'],
		payload: {
		request: {
			url: '/api/game',
			method: 'POST',
			headers: {
				'Content-Type': 'application/json'
			},
			data: {
				game_name: fields.game_name,
				game_token: fields.game_token + "1-gtkoen",
				destination_url: fields.destination_url
			}
		}
		}
	}
}

AxiosMiddlewareOptions

const axiosMiddlewareOptions = {
    interceptors: {
    request: [
        (action, config) => {
            if (action.getState().auth.accessToken) {
                config.headers['x-access-token'] = action.getState().auth.accessToken
            }
        
        return config
        }
    ]
    }
}

axiosMiddleware(client, axiosMiddlewareOptions),

It returns this:

{
    payload: {
    request: {
        data {
            destination_url: 'url',
            game_name: 'gname',
            game_token: 'token'
        },
        headers {
            Content-Type: 'application/json',
        }
    }
    },
    types: ['CREATE_GAME', 'CREATE_GAME_SUCCESS', 'CREATE_GAME_ERROR']
}

My question is:
Where is my x-auth-token and why i dont get response from url? Thanks :)

Is it possible to send custom headers?

we're trying to send custom headers for NTML authentification purposes. I've tried this

const client = axios.create({ //all axios can be used, shown in axios documentation baseURL: 'http://1.2.3.4:8080', options: { responseType: 'json', SamAccount: 'username' } });

and
const client = axios.create({ //all axios can be used, shown in axios documentation baseURL: 'http://1.2.3.4:8080', options: { responseType: 'json', headers: { 'SamAccount': 'username' }; } });

but we're not seeing the headers change. the options property seems to be how it is done in the axios documentation... are there any other things that we should try?

How to use authentication headers?

First of all: thank you for creating this middleware!

Let's say I have a JWT token stored in the redux tree (fetched/generated on the login) that is needed for certain request to succeed - is it possible to somehow pass/use the token with this middleware?

How to handle _FAIL actions

I am following the same pattern used in the middleware-netcore-example. I see _FAIL constants in the app.ts actions file. I am attempting to send failures into state, but I am getting my wires crossed.

state:
export type App = { readonly values: { name: string, value: number }[] _readonly errors: [{ message: string, code: number }]_ };

action:
export type Action = { type: 'GET_VALUES_FAIL', payload: AxiosResponse } | ... };

reducer:
case Actions.GET_VALUES_FAIL: return { ...state, _errors_: action.payload.data };

Ideally i would map the fail action to a list of errors in the state..my app i have a error message action that ties into App.errors. Just not sure now to move forward given the errors live in a different object in state. How can i add this error data to my state?

Multiple API configuration

How would you use this middleware with multiple APIs

Is this possible? I don't think it is but maybe we could name space axios clients?

Thoughts?

please provide example using middlewareConfig

My GET is failing with redux-axios-middleware but when I make a simple call with what I believe is the same URL but with just an axios.get, it works. I think an interceptor will help me but I don't understand how to use it.

It would be really helpful if you could update the readme to include usage of the middlewareConfig you define in the readme.

Middleware options doesn't seems to work

I have the following code.

import axios from 'axios'
import axiosMiddleware from 'redux-axios-middleware'

const client = axios.create({
  baseURL: 'http://api.dev',
  responseType: 'json'
})

const options = {
  successSuffix: 'success',
  errorFix: 'error'
}

export default axiosMiddleware(client, options)

When I dispatch an action as { type: 'load' } then I get { type: 'load_FAIL' }. I expected to get { type: 'load_error' }.

Allow passing options to action

Idea is to allow passing options like returnRejectedPromiseOnError to action.

{
  type: 'LOAD',
  payload: {
    request:{
      url:'/categories',
      options: {
        returnRejectedPromiseOnError: true
      }
    }
  }
}

This way user can override client options in middleware setup. Options will be merged:

default options => middleware options => action options

Promise not rejected on api error

Hi,
i'm trying to catch api error using axios-middleware
server is responding with 404 however, promise is resolved instead of beeing rejected.
note that it's wrapped with other promise:

this: is not working:

Keychain
  .getGenericPassword()
  .then((credentials) => {
    return this.props.authProps(credentials.password).catch(() => {
      // handle error
    })
  })

workaround for this is to check in then callback if there is error key

Keychain
  .getGenericPassword()
  .then((credentials) => {
    return this.props.authProps(credentials.password).then((payload) => {
      if(!payload.error) { 
        // handle error 
      }
  })

Actions are dispatched correctly to store, just problem with this promise

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.