T
TanStack3mo ago
exotic-emerald

useMutation in Context+Provider infinite calls

Hi all, thanks in advance for your time. I'm using react-router & tanstack query. I need a POST to get fired off upon App initialisation and I figured I could do that using a createContext and provider to then be able to access it a bit deeper down easily, that'd be useful. I am completely unable to get this to work or understand why I am getting infinite re-renders. I have tried everything chat gippity is suggesting and have googled as much as possible. In AuthContext.tsx:
import { useMutation } from "@tanstack/react-query";
import { getFingerprint } from "@thumbmarkjs/thumbmarkjs";
import { createContext, useState, type ReactNode } from "react";

type TAuthContext = {
hasSession: boolean;
};

const AuthContext = createContext<TAuthContext | undefined>(undefined);

type TAuthProviderProps = {
children: ReactNode;
};

function AuthProvider({ children }: TAuthProviderProps) {
const [hasSession, setHasSession] = useState<boolean>(false);
const mutation = useMutation({
mutationFn: async () => {
const fingerprint = await getFingerprint();
const response = await fetch(
"http://localhost:8000/api/v1/auth/session/",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
body: JSON.stringify({
fingerprint: fingerprint,
}),
}
);
if (!response.ok) {
throw new Error();
} else {
return await response.json();
}
},
onSuccess: () => {
setHasSession(true);
},
onError: () => {
console.log("Error!");
},
});

if (!hasSession) {
// mutation.mutate(); BOOM, infinite calls
// setHasSession(true) KIND of works here, but it behaves weirdly in NotFound routes ("*")
}

return (
<AuthContext.Provider value={{ hasSession }}>
{children}
</AuthContext.Provider>
);
}

export { AuthProvider, AuthContext };
import { useMutation } from "@tanstack/react-query";
import { getFingerprint } from "@thumbmarkjs/thumbmarkjs";
import { createContext, useState, type ReactNode } from "react";

type TAuthContext = {
hasSession: boolean;
};

const AuthContext = createContext<TAuthContext | undefined>(undefined);

type TAuthProviderProps = {
children: ReactNode;
};

function AuthProvider({ children }: TAuthProviderProps) {
const [hasSession, setHasSession] = useState<boolean>(false);
const mutation = useMutation({
mutationFn: async () => {
const fingerprint = await getFingerprint();
const response = await fetch(
"http://localhost:8000/api/v1/auth/session/",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
credentials: "include",
body: JSON.stringify({
fingerprint: fingerprint,
}),
}
);
if (!response.ok) {
throw new Error();
} else {
return await response.json();
}
},
onSuccess: () => {
setHasSession(true);
},
onError: () => {
console.log("Error!");
},
});

if (!hasSession) {
// mutation.mutate(); BOOM, infinite calls
// setHasSession(true) KIND of works here, but it behaves weirdly in NotFound routes ("*")
}

return (
<AuthContext.Provider value={{ hasSession }}>
{children}
</AuthContext.Provider>
);
}

export { AuthProvider, AuthContext };
1 Reply
exotic-emerald
exotic-emeraldOP3mo ago
main.tsx:
import "./main.css";
import App from "./App";

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { AuthProvider } from "./features/auth/AuthContext";

const queryClient = new QueryClient();

createRoot(document.getElementById("root")!).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<AuthProvider>
<App />
</AuthProvider>
</QueryClientProvider>
</StrictMode>
);
import "./main.css";
import App from "./App";

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { AuthProvider } from "./features/auth/AuthContext";

const queryClient = new QueryClient();

createRoot(document.getElementById("root")!).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<AuthProvider>
<App />
</AuthProvider>
</QueryClientProvider>
</StrictMode>
);
App.tsx
import { BrowserRouter, Route, Routes } from "react-router";

import { Suspense, lazy } from "react";

const OnboardingForm = lazy(
() => import("./components/onboarding/OnboardingForm.tsx")
);

const NotFoundPage = lazy(() => import("./notFoundPage.tsx"));

export default function App() {
return (
<BrowserRouter>
<Routes>
<Route
path="/onboarding"
element={
<Suspense fallback={<div>Loading onboarding form...</div>}>
<OnboardingForm />
</Suspense>
}
/>
<Route path="*" element={<NotFoundPage />} />
</Routes>
</BrowserRouter>
);
}
import { BrowserRouter, Route, Routes } from "react-router";

import { Suspense, lazy } from "react";

const OnboardingForm = lazy(
() => import("./components/onboarding/OnboardingForm.tsx")
);

const NotFoundPage = lazy(() => import("./notFoundPage.tsx"));

export default function App() {
return (
<BrowserRouter>
<Routes>
<Route
path="/onboarding"
element={
<Suspense fallback={<div>Loading onboarding form...</div>}>
<OnboardingForm />
</Suspense>
}
/>
<Route path="*" element={<NotFoundPage />} />
</Routes>
</BrowserRouter>
);
}
This is my first React project, please bear with. Really appreciate any help, been wrestling with this all day. If I call mutation.mutateAsync in the if statement I do get somewhat of a correct behaviour, but the issue is twofold: - The docs say this should be awaited, it returns a promise. - It gets called once on my onboarding form but three times on my 404 Page.

Did you find this page helpful?