英文:
Unable to make a call inside a middleware with RTK-Query
问题
我需要,在用户登录后,进行/me
调用以获取他的属性。
我解决了,编写了一个返回null的React组件:
import { useEffect } from "react";
import { useLazyGetMeQuery } from "../features/apiSlice";
export const Me = () => {
const [getMe, data] = useLazyGetMeQuery();
useEffect(() => {
if (!data.data) {
getMe(undefined, true);
}
}, [data]);
return null;
};
export default Me;
它有效。但我不喜欢有一个返回null的组件的想法。
所以,我想使用中间件来进行这个调用,但是不成功。
这是我的第一个尝试:
import { createListenerMiddleware } from "@reduxjs/toolkit";
import { useDispatch } from "react-redux";
import { ACTION_SET_ACCESS_TOKEN } from "../utils/constants/constant";
import { apiSlice } from "../features/apiSlice";
const type = `accessTokenSlice/${ACTION_SET_ACCESS_TOKEN}`;
const dispatch = useDispatch();
const meMiddleware = createListenerMiddleware();
meMiddleware.startListening({
type,
effect: () => {
dispatch(apiSlice.util.updateQueryData('getMe', undefined, true));
}
});
export default meMiddleware;
这是第二个:
import { createListenerMiddleware } from "@reduxjs/toolkit";
import { ACTION_SET_ACCESS_TOKEN } from "../utils/constants/constant";
import { apiSlice } from "../features/apiSlice";
const type = `accessTokenSlice/${ACTION_SET_ACCESS_TOKEN}`;
const [getMe] = apiSlice.endpoints.getMe.useLazyQuery();
const meMiddleware = createListenerMiddleware();
meMiddleware.startListening({
type,
effect: () => {
getMe(undefined, true);
}
});
export default meMiddleware;
在这两种情况下,我得到了警告act.development.js:209 Warning: Invalid hook call. Hooks can only be called inside of the body of a function component.
和错误caught TypeError: Cannot read properties of null (reading 'useContext')
。
中间件是这样被调用的:
import { configureStore } from "@reduxjs/toolkit";
import { accessTokenSlice } from "../features/accessTokenSlice";
import { userSlice } from "../features/userSlice";
import addAccessTokenMiddleware from "../middlewares/addAccessTokenMiddleware";
import removeAccessTokenMiddleware from "../middlewares/removeAccessTokenMiddleware";
import meMiddleware from "../middlewares/meMiddleware";
import { apiSlice } from "../features/apiSlice";
const setupStore = (preloadedState) =>
configureStore({
reducer: {
[apiSlice.reducerPath]: apiSlice.reducer,
accessTokenSlice: accessTokenSlice.reducer,
userSlice: userSlice.reducer
},
preloadedState,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
immutableCheck: false,
serializableCheck: false
})
.prepend(addAccessTokenMiddleware.middleware)
.prepend(removeAccessTokenMiddleware.middleware)
.concat(apiSlice.middleware)
.concat(meMiddleware.middleware)
});
export default setupStore;
除了中间件(如果可能的话)或一个“null”React组件之外,是否有其他的方式来进行单个调用 - 无需刷新数据 - 例如,在用户登录后,但使用RTK Query?
英文:
I need, after a user has logged in, make a /me
call to get his property.
I solved writing a React Component that returns null:
import { useEffect } from "react";
import { useLazyGetMeQuery } from "../features/apiSlice";
export const Me = () => {
const [getMe, data] = useLazyGetMeQuery();
useEffect(() => {
// If there are no data, trigger it
/* istanbul ignore next */
if (!data.data) {
getMe(undefined, true);
}
}, [data]);
return null;
};
export default Me;
It works. But I don't like the idea to have a Component returning null.
So, I would use a middleware to make this call but cannot.
This is my first try:
import { createListenerMiddleware } from "@reduxjs/toolkit";
import { useDispatch } from "react-redux";
import { ACTION_SET_ACCESS_TOKEN } from "../utils/constants/constant";
import { apiSlice } from "../features/apiSlice";
const type = `accessTokenSlice/${ACTION_SET_ACCESS_TOKEN}`;
const dispatch = useDispatch();
const meMiddleware = createListenerMiddleware();
meMiddleware.startListening({
type,
effect: () => {
dispatch(apiSlice.util.updateQueryData('getMe', undefined, true));
}
});
export default meMiddleware;
And this is the second one:
import { createListenerMiddleware } from "@reduxjs/toolkit";
import { ACTION_SET_ACCESS_TOKEN } from "../utils/constants/constant";
import { apiSlice } from "../features/apiSlice";
const type = `accessTokenSlice/${ACTION_SET_ACCESS_TOKEN}`;
const [getMe] = apiSlice.endpoints.getMe.useLazyQuery();
const meMiddleware = createListenerMiddleware();
meMiddleware.startListening({
type,
effect: () => {
getMe(undefined, true);
}
});
export default meMiddleware;
On both case, I get the warning act.development.js:209 Warning: Invalid hook call. Hooks can only be called inside of the body of a function component.
and the error caught TypeError: Cannot read properties of null (reading 'useContext')
.
The middleware is recalled as
import { configureStore } from "@reduxjs/toolkit";
import { accessTokenSlice } from "../features/accessTokenSlice";
import { userSlice } from "../features/userSlice";
import addAccessTokenMiddleware from "../middlewares/addAccessTokenMiddleware";
import removeAccessTokenMiddleware from "../middlewares/removeAccessTokenMiddleware";
import meMiddleware from "../middlewares/meMiddleware";
import { apiSlice } from "../features/apiSlice";
const setupStore = (preloadedState) =>
configureStore({
reducer: {
[apiSlice.reducerPath]: apiSlice.reducer,
accessTokenSlice: accessTokenSlice.reducer,
userSlice: userSlice.reducer
},
preloadedState,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
immutableCheck: false,
serializableCheck: false
})
.prepend(addAccessTokenMiddleware.middleware)
.prepend(removeAccessTokenMiddleware.middleware)
.concat(apiSlice.middleware)
.concat(meMiddleware.middleware)
});
export default setupStore;
Other than middleware (if is it possible) or a "null" React component, is there some other manner to do a single call - without need of refresh data -, e.g. after an user has logged in, but using RTK Query?
答案1
得分: 1
根据官方文档,RTK Query是一个强大的数据获取和缓存工具。
这意味着您无需担心从后端缓存数据,RTKq将对其进行缓存并避免不必要的查询。只需在需要用户属性的组件中使用useQuery钩子:
const {
data: apiResponse,
isFetching,
isLoading,
} = useGetMeQuery();
通过options
参数和标签设置缓存更新规则(如果需要)。RTKq将仅在具有实际缓存时执行新的后端查询。
如果需要在用户退出应用程序时保存缓存状态并在返回时重新启动它(换句话说,在刷新浏览器选项卡后不要发出新的http请求),请参阅redux-persist。
更新:
如果仍然需要在中间件中填充缓存,请尝试以下代码:
const meMiddleware = createListenerMiddleware();
meMiddleware.startListening({
type,
effect: async (action, listenerApi) => {
listenerApi.cancelActiveListeners();
listenerApi.dispatch(
apiSlice.endpoints.getMe.initiate([arguments]), //将参数传递给您的端点
);
},
});
英文:
According to official doc,
> RTK Query is a powerful data fetching and caching tool
It means you don't need to cary about caching data from your backend, RTKq will cache it and avoid unnecessary queries. Just use useQuery hook in component, where you need user property:
const {
data: apiResponse,
isFetching,
isLoading,
} = useGetMeQuery();
set rules for cache update (if you need) by options
param and tags. RTKq will not execute new query to backend still it have actual cache.
If you need to save the cache state until the user exits the application and rehydrate it when return (in other words, don't make a new http request after, for example, refreshing browser tab), see redux-persist.
UPDATED:
If you still need to fill cache in a middleware, try this:
const meMiddleware = createListenerMiddleware();
meMiddleware.startListening({
type,
effect: async (action, listenerApi) => {
listenerApi.cancelActiveListeners();
listenerApi.dispatch(
apiSlice.endpoints.getMe.initiate([arguments]), //pass args to your endpoint
);
},
});
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论