how to pass react hook objects to axios interceptor from index.js in react

Viewed 923

I want to use axios request and response interceptor to show/hide loader before and after each request. For this I using redux to maintain loader state, But I can not use useDispatch and useHistory hooks inside interceptor file.

So I thought I will have to pass it from index.js, but again I am getting same error.

Invalid hook call. Hooks can only be called inside of the body of a function component

interceptor code.

const handler401 = (dispatch, history) => {
  dispatch(authActions.logOut);
  history.push(LOGIN);
};

const handler500 = (dispatch, error) => {
  let message = L('global.error');
  if (error.response && error.response.data?.error) {
    message = error.response.data.error;
  }
  dispatch(
    uiActions.showNotification({
      title: L('global.errorTitle'),
      message,
      status: 'error',
    })
  );
};

const setupInterceptors = (dispatch, history) => {

  request.api.interceptors.request.use(
    (successfulReq) => {
      console.log('show loader start');
      dispatch(uiActions.showLoader());
      return successfulReq;
    },
    (error) => {
      dispatch(uiActions.hideLoader());
      return Promise.reject(error);
    }
  );

  request.api.interceptors.response.use(
    (response) => {
      dispatch(uiActions.hideLoader());
      return response;
    },
    (error) => {
      console.log('hide loader start');
      dispatch(uiActions.hideLoader());
      if (error.response.status === 401) {
        handler401(dispatch, history);
      } else {
        handler500(dispatch, error);
      }
      return Promise.reject(error);
    }
  );
};

I have register this interceptor in index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider, useDispatch } from 'react-redux';
import { BrowserRouter, useHistory } from 'react-router-dom';
import App from './App';
import reportWebVitals from './reportWebVitals';
import store from './store/index';
import setupInterceptors from './services/interceptorService';

const history = useHistory();
const dispatch = useDispatch();
setupInterceptors(dispatch, history);

ReactDOM.render(
  <Provider store={store}>
    <React.StrictMode>
      <BrowserRouter>
        <App />
      </BrowserRouter>
    </React.StrictMode>
  </Provider>,
  document.getElementById('root')
);
reportWebVitals();

How can pass above two hooks to my interceptor?

1 Answers

Not sure if what you're doing is proper, but if you really wanna do it that way, you got to pass your dispatch and history functions only inside a React component.

Recommend you do something like this:

const App = () => {

  const dispatch = useDispatch()
  const history = useHistory()

  useEffect(() => {
    const eventHandlerFn = (success) => {
      dispatch(myAction.doSomething())
      history.push("/yay")
    }

    request.api.interceptors.request.use(eventHandlerFn)
    return () => {
      request.api.interceptors.request.eject(eventHandlerFn);
    }
  }, [])

  return (
  <div>
    <h1>My App</h1>
  </div>
  )
}

Here, I added the event handler function to your axios interceptor inside a useEffect with [] dependencies, so it only runs on App mount, which means it only runs once in the entire app.

Since I'm doing it inside a React component, I can pass the stuff that were hooked in, like your dispatch and history, to your setupInterceptor function.

The useEffect returns a function, cleans up your interceptor event handlers when App unmounts.

Related