Closed atalayio closed 3 months ago
I'm confused. If you close and reopen the app, you should be getting a brand new Redux store and JS environment. Are you persisting the Redux state and RTK Query data somehow?
I'm confused. If you close and reopen the app, you should be getting a brand new Redux store and JS environment. Are you persisting the Redux state and RTK Query data somehow?
Nope, I don't do any cache operation, but isFetching true continues to come even when I close and open the application. I don't know how and I can't prevent it.
I'm confused. If you close and reopen the app, you should be getting a brand new Redux store and JS environment. Are you persisting the Redux state and RTK Query data somehow?
Please check the video:
https://github.com/reduxjs/redux-toolkit/assets/106342678/02ad053e-1a04-4df0-b57e-fe7dd3a314a5
Please show your store setup. It's very likely that you use something like redux-persist
, which would need extra setup for RTK Query.
Please show your store setup. It's very likely that you use something like
redux-persist
, which would need extra setup for RTK Query.
This is my _layout.tsx;
import { useEffect } from "react";
import * as SplashScreen from "expo-splash-screen";
import { ErrorBoundary, router, Slot } from "expo-router";
import { StatusBar } from "expo-status-bar";
import { useFonts } from "expo-font";
import FontAwesome from "@expo/vector-icons/FontAwesome";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/es/integration/react";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import {
initialWindowMetrics,
SafeAreaProvider,
} from "react-native-safe-area-context";
import reduxStore, {
useAppSelector,
} from "../store";
import {
AlertContextProvider,
ThemeContextProvider,
useAlertContext,
HeaderRightContextProvider,
NotifyContextProvider,
} from "../contexts";
import { Alert } from "../components/alert";
import { Notify } from "../components/notify";
SplashScreen.preventAutoHideAsync();
const RootLayout = () => {
const { store, persistor } = reduxStore();
const [loaded, error] = useFonts({
SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
...FontAwesome.font,
});
useEffect(() => {
if (error) throw error;
}, [error]);
if (!loaded) return null;
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<MiddlewareLayout />
</PersistGate>
</Provider>
</GestureHandlerRootView>
);
};
const MiddlewareLayout = () => {
return (
<NotifyContextProvider>
<AlertContextProvider>
<HeaderRightContextProvider>
<InitialLayout />
</HeaderRightContextProvider>
</AlertContextProvider>
</NotifyContextProvider>
);
};
const InitialLayout = () => {
const { theme } = useAppSelector((x) => x.app);
const { auth } = useAppSelector((x) => x.user);
const {
open: isAlertOpen,
options: alertOptions,
onClose: onCloseAlert,
} = useAlertContext();
// const { updateProgress } = useUpdate();
useEffect(() => {
if (!auth) router.replace("/login");
SplashScreen.hideAsync();
}, []);
return (
<ThemeContextProvider>
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
<Alert
isOpen={isAlertOpen}
options={alertOptions}
onClose={onCloseAlert}
/>
<Notify />
<Slot />
<StatusBar style={theme === "dark" ? "light" : "dark"} />
</SafeAreaProvider>
</ThemeContextProvider>
);
};
export { ErrorBoundary };
export default RootLayout;
and this is my store/index.ts
import { combineReducers, configureStore } from "@reduxjs/toolkit";
import { useDispatch, useSelector, TypedUseSelectorHook } from "react-redux";
import { persistStore, persistReducer } from "redux-persist";
import AsyncStorage from "@react-native-async-storage/async-storage";
import app, { changeApi, themeChange, useDeviceThemeChange } from "./app";
import user, { userLogin, userLogout } from "./user";
import { api } from "../api/api";
const rootReducer = combineReducers({
app,
user,
[api.reducerPath]: api.reducer,
});
const persistConfig = {
key: "root",
storage: AsyncStorage,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
export type RootState = ReturnType<typeof rootReducer>;
const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false,
immutableCheck: false,
}).concat(api.middleware),
});
export type AppDispatch = typeof store.dispatch;
export const useAppDispatch: () => AppDispatch = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
export { changeApi, themeChange, useDeviceThemeChange, userLogin, userLogout };
export default () => {
let persistor = persistStore(store);
return { store, persistor };
};
Why do you say that you are not using persistance? Pretty much every second word there is persist
.
You need to apply the steps described at this documentation page. If you don't, you get the behaviour you are currently seeing. https://redux-toolkit.js.org/rtk-query/usage/persistence-and-rehydration#redux-persist
Why do you say that you are not using persistance? Pretty much every second word there is
persist
.You need to apply the steps described at this documentation page. If you don't, you get the behaviour you are currently seeing. https://redux-toolkit.js.org/rtk-query/usage/persistence-and-rehydration#redux-persist
Hello, I tried your solution for now and getting this; code:
import isomorphicFetch from "isomorphic-fetch";
import { Mutex } from "async-mutex";
import {
BaseQueryFn,
createApi,
FetchArgs,
fetchBaseQuery,
FetchBaseQueryError,
} from "@reduxjs/toolkit/query/react";
import { RootState } from "../store";
import { Action } from "@reduxjs/toolkit";
import { REHYDRATE } from "redux-persist";
function isHydrateAction(action: Action): action is Action<typeof REHYDRATE> & {
key: string
payload: RootState
err: unknown
} {
return action.type === REHYDRATE
}
export const baseQuery = (baseUrl: string) =>
fetchBaseQuery({
baseUrl,
fetchFn: isomorphicFetch,
prepareHeaders: (headers, { getState }) => {
const state = getState() as RootState;
const token = (getState() as RootState).user.user?.token;
headers.set("Content-Type", "application/json");
if (token) {
headers.set("Authorization", `Bearer ${token}`);
}
return headers;
},
});
const mutex = new Mutex();
export const baseQueryWithReauth: BaseQueryFn<
string | FetchArgs,
unknown,
FetchBaseQueryError
> = async (args, api, extraOptions) => {
const apiUrl = (api.getState() as RootState).app.api;
if (!apiUrl) {
return {
error: {
status: 400,
statusText: "Bad Request",
data: "No Url found",
},
};
}
await mutex.waitForUnlock();
let result = await baseQuery(apiUrl)(args, api, extraOptions);
if (result.error) {
if (!mutex.isLocked()) {
const release = await mutex.acquire();
try {
result = await baseQuery(apiUrl)(args, api, extraOptions);
} finally {
release();
}
}
}
return result;
};
export const api = createApi({
baseQuery: baseQueryWithReauth,
extractRehydrationInfo(action, { reducerPath }): any {
if (isHydrateAction(action)) {
if (action.key === 'key used with redux-persist') {
return action.payload
}
return action.payload[api.reducerPath]
}
},
endpoints: () => ({}),
});
error: TypeError: Cannot convert undefined value to object, js engine: hermes
What line are you getting that error on? Those two return statements seem suspicious to me - if an action is not exactly the right action, you shouldn't return anything from extractRehydrationInfo
.
What line are you getting that error on? Those two return statements seem suspicious to me - if an action is not exactly the right action, you shouldn't return anything from
extractRehydrationInfo
.export const api = createApi({ baseQuery: baseQueryWithReauth, extractRehydrationInfo(action, { reducerPath }): any { if (isHydrateAction(action)) { if (action.key === 'key used with redux-persist') { return action.payload } // return action.payload[api.reducerPath] } }, endpoints: () => ({}), });
As you said, I put the second return in the comment line, now there is no error when I start the application even if I have any internet problems with isFetching. Thank you so much! ❤️
I check isFetching with RTk Query and add the loading component accordingly, but sometimes when I experience internet problems (such as the internet suddenly cutting off), isFetching remains true forever. Even if I close and reopen the application, it doesn't work. How can I go about this? Here is the code: