useMemo should not be used for side effects.
In strict mode during development on React 17, function component bodies and functions passed to useState, useMemo, or useReducer are invoked twice.
Invocations of a functional component happen before any useEffect within it is resolved. Note that in the following code in React 18, in strict mode, "render" will be logged twice first and then "effect" will be logged twice:
import { useEffect } from "react";
export default function App() {
console.log("render");
useEffect(() => console.log("effect"));
return null;
}
https://codesandbox.io/s/use-effect-order-strict-mode-6v00ys?file=/src/App.js:0-152
(note that in React 17, the console is muted on one of the invocations, which caused me a lot of pain during debugging - this isn't the case in React 18).
In React 18, functions passed to useEffect are also called twice. I'm not sure if this is dev mode only.
These are all intentional in order to detect issues e.g. with async react and upcoming features such as preserving state between unmounts.
We need to set loading=true within the hook depending on whether fetch is going to be called, and then return this value from the hook.
Perhaps we need to call fetch with useSyncExternalStore (via the separate module for React 17 compatibility) although as we're not caching responses, I don't think we would have an external store, so perhaps this isn't relevant.
...or the upcoming useEvent if/when it makes it in to React.