英文:
Dispatch is not working when it is placing in Axios interceptor
问题
我在axios拦截器中使用dispatch事件,所以每当我从API得到400错误时,我会将事件分派到UI以通知消息和重定向。请参考
export const loggerInterceptor = (AXIOS) => {
  const { request, response } = AXIOS.interceptors;
  const token = getCookie(COOKIE_JWT_TOKEN);
  
  request.use((config) => {
    config.performance = calculateTimes();
    config.timestamp = Date.now();
    return config;
  });
  
  response.use(
    (response) => {
      if (response) {
        flightInfo = {
          ...flightInfo,
          ...response.data,
        };
      }
      return Promise.resolve(response);
    },
    (error) => {
      if (error.response.status === 400) {
        addErrorToast();
        window.location.href = window.location.origin + '/app';
      }
      return Promise.reject(error);
    }
  );
};
以下的addErrorToast在我得到400错误时触发正常,但是分派没有发生。有什么解决方案或建议来解决这个问题吗?
const addErrorToast = () => {
  const dispatch = useDispatch();
  dispatch(
    addToast({
      type: "success",
      msg: (
        <FormattedMessage
          id="notifications.connected"
          defaultMessage="You are connected!"
        />
      ),
      hasCloseIcon: false,
    })
  );
};
英文:
I am using dispatch event in axios interceptor, so that whenever i got 400 error from api i will dispatching the event to UI to notify the message and redirection as well.
Please refer
export const loggerInterceptor = (AXIOS ) => {
      const { request, response } = AXIOS.interceptors;
      const token = getCookie(COOKIE_JWT_TOKEN);
      
      request.use((config) => {
        config.performance = calculateTimes();
        config.timestamp = Date.now();
        return config;
      });
    
      response.use(
        (response) => {
          if (
            response
          ) {
            flightInfo = {
              ...flightInfo,
              ...response.data,
            };
          }
          return Promise.resolve(response);
        },
        (error) => {
         if(error.response.status===400)
          {
            addErrorToast();
           window.location.href= window.location.origin +'/app';
            
          } 
          return Promise.reject(error);
        }
      );
    };
The below addErrorToast is triggering properly when i get 400 error but dispatch is not happening. Any solutions or suggestions to fix this
const addErrorToast = ()=>{
  const dispatch = useDispatch();
  dispatch(
          addToast({
            type: "success",
            msg: (
              <FormattedMessage
                id="notifications.connected"
                defaultMessage="You are connected!"
              />
            ),
            hasCloseIcon: false,
          })
      );
}
答案1
得分: 2
你是否在Redux提供程序包装器之外导入此Axios配置?这是一个常见的错误,因为此配置通常会在基本的 index.tsx/main.tsx 文件中导入,而Redux包装器也会在相同文件或 App.tsx 文件中设置,而在这两种情况下,您的Axios配置将无法进行dispatch。
确保在Redux提供程序包装器内导入此axios配置。您还希望将此拦截器转换为钩子,以便它可以使用其他钩子。
以下部分无需翻译,因为它们是代码示例。
"WORKING EXAMPLE: React18 + Redux + Axios Interceptor dispatch"
"I created a sandbox with a working example of this situation where an Axios interceptor needs to dispatch to Redux."
接下来是一些代码示例,其中包括 index.js、app.js、useAxiosInterceptor.js、store.js 和 counterSlice.js 的代码。这些部分无需翻译,因为它们是代码示例。
最后是 package.json 的内容,也无需翻译,因为它是一个JSON配置文件。
希望这些信息对您有所帮助!
英文:
Are you importing this Axios config outside of your Redux Provider wrapper? It's an easy mistake to make since this config would typically be imported in the base index.tsx/main.tsx file and the Redux wrapper set in the same file or App.tsx which in both cases your Axios config will not be able to dispatch.
Make sure you import this axios config within the Redux Provider wrapper. You will also want to turn your interceptor into a hook so it can use other hooks.
This won't work:
index.js
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import { Provider } from 'react-redux'
import store from './store'
import App from './App';
import initAxios from './config/axios';
initAxios(); <--- Not proper hook format (i.e. useXYZ)
ReactDOM.render(
  <BrowserRouter>
    <Provider store={store}>   <--- Store does not wrap Axios
      <App />
    </Provider>
  </BrowserRouter>,
  document.getElementById('root'),
);
<!-- end snippet -->
WORKING EXAMPLE: React18 + Redux + Axios Interceptor dispatch
I created a sandbox with a working example of this situation where an Axios interceptor needs to dispatch to Redux.
https://codesandbox.io/p/sandbox/react18-axios-interceptor-dispatch-to-redux-jiin13
index.js
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import store from "./store";
import { Provider } from "react-redux";
/*
// axios interceptor must be contained within the redux store, App.js
import useAxiosInterceptor from "./useAxiosInterceptor";
useAxiosInterceptor();
*/
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
  <StrictMode>
    {" "}
    {/* <--- 2 renders expected */}
    <Provider store={store}>
      <App />
    </Provider>
  </StrictMode>
);
<!-- end snippet -->
app.js
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
import { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import { decrement, increment } from "./counterSlice";
import axios from "axios";
import useAxiosInterceptor from "./useAxiosInterceptor";
export default function App() {
  useAxiosInterceptor();
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();
  useEffect(() => {
    (async () => {
      try {
        const results = await axios.get("https://www.google.com");
        console.log({ results });
      } catch (e) {
        // console.log(e);
      }
    })();
  }, []);
  return (
    <div>
      <div>
        <button
          aria-label="Increment value"
          onClick={() => dispatch(increment())}
        >
          Increment
        </button>
        <span>{count}</span>
        <button
          aria-label="Decrement value"
          onClick={() => dispatch(decrement())}
        >
          Decrement
        </button>
      </div>
    </div>
  );
}
<!-- end snippet -->
useAxiosInterceptor.js
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
import axios from "axios";
import { useDispatch } from "react-redux";
import { increment } from "./counterSlice";
const useAxiosInterceptor = () => {
  const dispatch = useDispatch();
  axios.interceptors.request.use((req) => {
    return req;
  });
  axios.interceptors.response.use((res) => {
    return res;
  });
  axios.interceptors.response.use(
    (res) => res,
    (err) => {
      console.log("##### AXIOS ERROR #####");
      dispatch(increment());
    }
  );
  return null;
};
export default useAxiosInterceptor;
<!-- end snippet -->
store.js
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counterSlice";
export default configureStore({
  reducer: {
    counter: counterReducer,
  },
});
<!-- end snippet -->
counterSlice.js
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
import { createSlice } from "@reduxjs/toolkit";
export const counterSlice = createSlice({
  name: "counter",
  initialState: {
    value: 0,
  },
  reducers: {
    increment: (state) => {
      // Redux Toolkit allows us to write "mutating" logic in reducers. It
      // doesn't actually mutate the state because it uses the Immer library,
      // which detects changes to a "draft state" and produces a brand new
      // immutable state based off those changes
      state.value += 1;
    },
    decrement: (state) => {
      state.value -= 1;
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload;
    },
  },
});
// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;
<!-- end snippet -->
package.json
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
{
  "name": "react",
  "version": "1.0.0",
  "description": "React example starter project",
  "keywords": [
    "react",
    "starter"
  ],
  "main": "src/index.js",
  "dependencies": {
    "@reduxjs/toolkit": "^1.9.2",
    "axios": "^1.3.2",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-redux": "^8.0.5",
    "react-scripts": "^5.0.1"
  },
  "devDependencies": {
    "@babel/runtime": "7.13.8",
    "typescript": "4.1.3"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}
<!-- end snippet -->
I hope that helps!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论