T
TanStack•10mo ago
fair-rose

Keep running useMutations while tab is not visible

I have a use case of uploading multiple files to my backend, we have one graphql mutation per upload. And we can support upload 300 files at a time. They get uploaded synchronously by using a mutation scope. Every single useMutation is in a separate component... and we have the desire to have the mutations run even when the tab is not visible (picture attached to help visualize) The only way I'm seeing to achieve this is with this code:
React.useEffect(() => {
focusManager.setFocused(true)
return () => {
if (!focusManager.isFocused()) {
focusManager.setFocused(false)
}
}
}, [])
React.useEffect(() => {
focusManager.setFocused(true)
return () => {
if (!focusManager.isFocused()) {
focusManager.setFocused(false)
}
}
}, [])
Is there anything wrong with this approach? I added the cleanup just in case...
No description
4 Replies
fair-rose
fair-rose•10mo ago
are you saying the mutations aren't starting when the tab is in the background ? because I think they should
fair-rose
fair-roseOP•10mo ago
i'm not seeing them start in the background. let me double check
fair-rose
fair-rose•10mo ago
Only retries are paused
fair-rose
fair-roseOP•10mo ago
https://codesandbox.io/p/sandbox/busy-flower-fkvpmj
import React from "react";
import { useMutation } from "@tanstack/react-query";

const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time));

function Download(props: { index: number }) {
const mutation = useMutation({
scope: {
id: "upload",
},
mutationFn: async () => {
await sleep(100);
return true;
},
onSuccess: () => {
console.log("success ", props.index);
},
});
const status = mutation.isPaused ? "paused" : mutation.status;
React.useEffect(() => {
if (process.env.NODE_ENV === "development") {
const timeoutId = setTimeout(() => {
mutation.mutate();
}, 0);

return () => {
clearTimeout(timeoutId);
};
} else {
mutation.mutate();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div
style={{
width: 200,
height: 40,
border: "1px solid black",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<p>{status}</p>
</div>
);
}

export default function App() {
return (
<div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
{new Array(300).fill(0).map((item, index) => (
<Download key={index} index={index} />
))}
</div>
);
}
import React from "react";
import { useMutation } from "@tanstack/react-query";

const sleep = (time) => new Promise((resolve) => setTimeout(resolve, time));

function Download(props: { index: number }) {
const mutation = useMutation({
scope: {
id: "upload",
},
mutationFn: async () => {
await sleep(100);
return true;
},
onSuccess: () => {
console.log("success ", props.index);
},
});
const status = mutation.isPaused ? "paused" : mutation.status;
React.useEffect(() => {
if (process.env.NODE_ENV === "development") {
const timeoutId = setTimeout(() => {
mutation.mutate();
}, 0);

return () => {
clearTimeout(timeoutId);
};
} else {
mutation.mutate();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div
style={{
width: 200,
height: 40,
border: "1px solid black",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
>
<p>{status}</p>
</div>
);
}

export default function App() {
return (
<div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
{new Array(300).fill(0).map((item, index) => (
<Download key={index} index={index} />
))}
</div>
);
}
idk why but it removed my tanstack dep hmmm this is also not really working when I do that setFocused 😦 i'm seeing when the tab is not visible.. so it's still open in my browser but not visible anymore (i'm focused on another) idk i the scope has anything to do with it, i'm supposed to process these synchronously
focusManager.subscribe((focused) => {
if (!focused) {
focusManager.setFocused(true);
}
});
focusManager.subscribe((focused) => {
if (!focused) {
focusManager.setFocused(true);
}
});
this is the fix just not sure about cleanup so that once this page is gone it's not overriding my focus stuff I think I landed on adding eventListener to "visibilitychange" and always setting focused to true

Did you find this page helpful?