英文:
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!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论