結論: 「<StrictMode/>
で囲まれてて」「開発ビルドのときだけ」マウント時に useEffect が2回発火するようになります。
あくまで検証が目的の変更
一見、マウント時にcleanupが走るのは無駄に思えますよね?その通りです。2回呼ばれる合理的な理由はありません。
ではなぜ useEffect が2回発火するのかというと、検証のためです。
実は React はマウント/アンマウント時以外にもuseEffectを呼び出すような API の導入を予定しています。つまり、以下のコードはマウント時以外にも呼ばれるようになります。
useEffect(() => { // mount 時以外も呼ばれる可能性がある! }, []);
(とはいえまだ当分の間はマウント時のみという認識でいいと思います。Offscreen APIなどが来るまでは大丈夫です)
こうなる予定があるので、「マウントからアンマウントまでに2回 useEffect が走っても問題ないか」の確認を促す意味で StrictMode で useEffect が2回実行されるようになった、ということらしいです。
実際、検証が目的なので production build では useEffect は2回実行されません。 ご安心ください。